Java 并发专题 : Semaphore 实现 互斥 与 连接池

来源:互联网 发布:自己设计算法 编辑:程序博客网 时间:2024/05/21 17:13

继续并发方面的知识。今天介绍Semaphore,同样在java.util.concurrent包下。

本来准备通过例子,从自己实现到最后使用并发工具实现,但是貌似效果并不是很好,有点太啰嗦的感觉,所有准备直入主题。

介绍:Semaphore中管理着一组虚拟的许可,许可的初始数量可通过构造函数来指定【new Semaphore(1);】,执行操作时可以首先获得许可【semaphore.acquire();】,并在使用后释放许可【semaphore.release();】。如果没有许可,那么acquire方法将会一直阻塞直到有许可(或者直到被终端或者操作超时)。

作用:可以用来控制同时访问某个特定资源的操作数量,或者某个操作的数量。

下面使用Semaphore实现两个例子:

1、互斥

大家都学过操作系统,都知道互斥的概念,比较简单的互斥实现,比如PV操作,判断资源,然后忙等实现互斥;上一篇博客也说过,忙等对CPU的消耗巨大,下面我们通过Semaphore来实现一个比较好的互斥操作:

假设我们公司只有一台打印机,我们需要对这台打印机的打印操作进行互斥控制:

[java] view plain copy
  1. package com.zhy.concurrency.semaphore;  
  2.   
  3. import java.util.concurrent.Semaphore;  
  4.   
  5. /** 
  6.  * 使用信号量机制,实现互斥访问打印机 
  7.  *  
  8.  * @author zhy 
  9.  *  
  10.  */  
  11. public class MutexPrint  
  12. {  
  13.   
  14.     /** 
  15.      * 定义初始值为1的信号量 
  16.      */  
  17.     private final Semaphore semaphore = new Semaphore(1);  
  18.   
  19.     /** 
  20.      * 模拟打印操作 
  21.      * @param str 
  22.      * @throws InterruptedException 
  23.      */  
  24.     public void print(String str) throws InterruptedException  
  25.     {  
  26.         //请求许可  
  27.         semaphore.acquire();  
  28.           
  29.         System.out.println(Thread.currentThread().getName()+" enter ...");  
  30.         Thread.sleep(1000);  
  31.         System.out.println(Thread.currentThread().getName() + "正在打印 ..." + str);  
  32.         System.out.println(Thread.currentThread().getName()+" out ...");  
  33.         //释放许可  
  34.         semaphore.release();  
  35.     }  
  36.   
  37.     public static void main(String[] args)  
  38.     {  
  39.         final MutexPrint print = new MutexPrint();  
  40.   
  41.         /** 
  42.          * 开启10个线程,抢占打印机 
  43.          */  
  44.         for (int i = 0; i < 10; i++)  
  45.         {  
  46.             new Thread()  
  47.             {  
  48.                 public void run()  
  49.                 {  
  50.                     try  
  51.                     {  
  52.                         print.print("helloworld");  
  53.                     } catch (InterruptedException e)  
  54.                     {  
  55.                         e.printStackTrace();  
  56.                     }  
  57.                 };  
  58.             }.start();  
  59.         }  
  60.   
  61.     }  
  62.   
  63. }  

输出结果:

[java] view plain copy
  1. Thread-1 enter ...  
  2. Thread-1正在打印 ...helloworld  
  3. Thread-1 out ...  
  4. Thread-2 enter ...  
  5. Thread-2正在打印 ...helloworld  
  6. Thread-2 out ...  
  7. Thread-0 enter ...  
  8. Thread-0正在打印 ...helloworld  
  9. Thread-0 out ...  
  10. Thread-3 enter ...  
  11. Thread-3正在打印 ...helloworld  
  12. Thread-3 out ...  

通过初始值为1的Semaphore,很好的实现了资源的互斥访问。

2、连接池的模拟实现

在项目中处理高并发时,一般数据库都会使用数据库连接池,假设现在数据库连接池最大连接数为10,当10个连接都分配出去以后,现在有用户继续请求连接,可能的处理:

a、手动抛出异常,用户界面显示,服务器忙,稍后再试

b、阻塞,等待其他连接的释放

从用户体验上来说,更好的选择当然是阻塞,等待其他连接的释放,用户只会觉得稍微慢了一点,并不影响他的操作。下面使用Semaphore模拟实现一个数据库连接池:

[java] view plain copy
  1. package com.zhy.concurrency.semaphore;  
  2.   
  3. import java.util.ArrayList;  
  4. import java.util.List;  
  5. import java.util.concurrent.Semaphore;  
  6. /** 
  7.  * 使用Semaphore模拟数据库链接池的使用 
  8.  * @author zhy 
  9.  * 
  10.  */  
  11. public class ConnectPool  
  12. {  
  13.     private final List<Conn> pool = new ArrayList<Conn>(3);  
  14.     private final Semaphore semaphore = new Semaphore(3);  
  15.   
  16.     /** 
  17.      * 初始化分配3个连接 
  18.      */  
  19.     public ConnectPool()  
  20.     {  
  21.         pool.add(new Conn());  
  22.         pool.add(new Conn());  
  23.         pool.add(new Conn());  
  24.     }  
  25.   
  26.     /** 
  27.      * 请求分配连接 
  28.      * @return 
  29.      * @throws InterruptedException 
  30.      */  
  31.     public Conn getConn() throws InterruptedException  
  32.     {  
  33.         semaphore.acquire();  
  34.         Conn c = null  ;  
  35.         synchronized (pool)  
  36.         {  
  37.             c = pool.remove(0);  
  38.         }  
  39.         System.out.println(Thread.currentThread().getName()+" get a conn " + c);  
  40.         return c ;  
  41.     }  
  42.       
  43.     /** 
  44.      * 释放连接 
  45.      * @param c 
  46.      */  
  47.     public void release(Conn c)  
  48.     {  
  49.         pool.add(c);  
  50.         System.out.println(Thread.currentThread().getName()+" release a conn " + c);  
  51.         semaphore.release();  
  52.     }  
  53.   
  54.     public static void main(String[] args)  
  55.     {  
  56.   
  57.         final ConnectPool pool = new ConnectPool();  
  58.           
  59.         /** 
  60.          * 第一个线程占用1个连接3秒 
  61.          */  
  62.         new Thread()  
  63.         {  
  64.             public void run()  
  65.             {  
  66.                 try  
  67.                 {  
  68.                     Conn c = pool.getConn();  
  69.                     Thread.sleep(3000);  
  70.                     pool.release(c);  
  71.                 } catch (InterruptedException e)  
  72.                 {  
  73.                     e.printStackTrace();  
  74.                 }  
  75.             };  
  76.         }.start();  
  77.         /** 
  78.          * 开启3个线程请求分配连接 
  79.          */  
  80.         for (int i = 0; i < 3; i++)  
  81.         {  
  82.             new Thread()  
  83.             {  
  84.                 public void run()  
  85.                 {  
  86.                     try  
  87.                     {  
  88.                         Conn c = pool.getConn();  
  89.                     } catch (InterruptedException e)  
  90.                     {  
  91.                         e.printStackTrace();  
  92.                     }  
  93.                 };  
  94.             }.start();  
  95.         }  
  96.   
  97.     }  
  98.   
  99.     private class Conn  
  100.     {  
  101.     }  
  102.   
  103. }  

[java] view plain copy
  1. Thread-0 get a conn com.zhy.concurrency.semaphore.ConnectPool$Conn@12b6651  
  2. Thread-2 get a conn com.zhy.concurrency.semaphore.ConnectPool$Conn@e53108  
  3. Thread-1 get a conn com.zhy.concurrency.semaphore.ConnectPool$Conn@1888759  
  4. Thread-0 release a conn com.zhy.concurrency.semaphore.ConnectPool$Conn@12b6651  
  5. Thread-3 get a conn com.zhy.concurrency.semaphore.ConnectPool$Conn@12b6651  
我们测试时,让Thread-0持有一个连接3秒,然后瞬间让3个线程再去请求分配连接,造成Thread-3一直等到Thread-0对连接的释放,然后获得连接。


通过两个例子,基本已经了解了Semaphore的用法,这里的线程池例子只是为了说明Semaphore的用法,真实的实现代码比这复杂的多,而且可能也不会直接用Semaphore。


好了,之后会继续Java并发的博客


Git:  https://github.com/whtchl/JavaConcurrentTemplate

原创粉丝点击