并发库应用之七 & 信号灯Semaphore应用
Semaphore可以维护当前访问自身的线程个数,并且提供了同步机制。
Semaphore实现的功能类似于厕所里有5个坑,有10个人要上厕所,同时就只能有5个人占用,当5个人中 的任何一个让开后,其中在等待的另外5个人中又有一个可以占用了。另外等待的5个人中可以是随机获得优先机会,也可以是按照先来后到的顺序获得机会,这取决于构造Semaphore对象时传入的参数选项(常见于 限流 机制场景应用中):
Semaphore(int permits)
Semaphore(int permits, boolean fair)
JDK包位置:java.util.concurrent.Semaphore
一个计数信号量。从概念上讲,信号量维护了一个许可集。如有必要,在许可可用前会阻塞每一个 acquire(),然后再获取该许可。每个 release() 添加一个许可,从而可能释放一个正在阻塞的获取者。但是,不使用实际的许可对象,Semaphore 只对可用许可的号码进行计数,并采取相应的行动。
Semaphore 通常用于限制可以访问某些资源(物理或逻辑的)的线程数目。例如,下面的类使用信号量控制对内容池的访问:
1 import java.util.concurrent.ExecutorService; 2 import java.util.concurrent.Executors; 3 import java.util.concurrent.Semaphore; 4 5 public class SemaphoreTest { 6 public static void main(String[] args) { 7 //线程数动态变化,来一个就产生一个线程 8 ExecutorService service = Executors.newCachedThreadPool(); 9 //可容纳的线程并发数 10 final Semaphore sp = new Semaphore(3); 11 for (int i = 0; i < 10; i++) { 12 Runnable runnable = new Runnable() { 13 public void run() { 14 try { 15 //从此信号量获取一个许可,在提供一个许可前一直将线程阻塞,否则线程被中断 16 sp.acquire(); 17 } catch (InterruptedException e1) { 18 e1.printStackTrace(); 19 } 20 System.out.println(String.format("线程%s进入,当前已有%d个并发", Thread.currentThread().getName(), (3 - sp.availablePermits()))); 21 try { 22 Thread.sleep((long) (Math.random() * 10000)); 23 } catch (InterruptedException e) { 24 e.printStackTrace(); 25 } 26 System.out.println(String.format("线程【%s】已经离开-------BYE BYE", Thread.currentThread().getName())); 27 sp.release();//释放一个许可,将其返回给信号量 28 } 29 }; 30 service.execute(runnable);//提交了10个任务放入到线程队列 31 } 32 try { 33 while (true) { 34 if (sp.availablePermits() == 3) { 35 System.out.println("****************所有线程执行完毕****************"); 36 break; 37 } 38 } 39 } finally { 40 service.shutdown(); 41 } 42 } 43 }
上述代码运行结果如下所示:
总结:传统互斥只能内部释放锁this.unlock(),进去this.lock()晕倒了别人就没法进去了;单个信号量的Semaphore对象也可以实现互斥锁的功能,并且可以是由一个线程获得了“锁”,再由另一个线程释放“锁”,sp.release() sp.acquire()。这可应用于死锁恢复的一些场合
提示:欢迎继续参看我相关的下一篇博客:并发库应用之八 & 循环路障CyclicBarrier应用