Semaphore:
类java.util.concurrent.Semaphore提供了一个计数信号量,从概念上讲,信号量维护了一个许可集,如有必要,在许可可用前会阻塞每一个acquire(),然后再获取该许可。每个release()添加一个许可,从而可能释放一个正在阻塞的获取者。但是,不使用实际的许可对象,Semaphore只对可用许可的号码进行计数,并采取相应的行动。调用acquire()时无法保持同步锁,因为这会阻止将数据项返回到池中。信号量封装所需的同步,以限制对池的访问,这同维持该池本身一致性所需的同步是分开的。将信号量初始化为1,使得它在使用时最多只有一个可用的许可,从而可用作一个相互排斥的锁。这通常也称为二进制信号量,因为它只能有两种状态。按此方式使用,二进制信号量具有某种属性(与很多Lock实现不同),即可以由线程释放“锁”,而不是由所有者(因为信号量没有所有权的概念)。在某些专门的上下文(如死锁恢复)中这会很有用。
Semaphore可以实现信号灯,可以维护当前访问自身的线程个数,并提供了同步机制。使用Semaphore可以控制同时访问资源的线程个数
Semaphore sp = new Semaphored(个数);
sp.availablePermits()当前可获得个数
sp.acquire();取得信号灯
sp.release();释放信号灯
1 import java.util.concurrent.ExecutorService; 2 import java.util.concurrent.Executors; 3 import java.util.concurrent.Semaphore; 4 5 public class SemaphoreTest { 6 7 /** 8 * @param args 9 */ 10 public static void main(String[] args) { 11 ExecutorService service = Executors.newCachedThreadPool(); 12 //最多3个许可,也就最多有3个线程同时访问 13 final Semaphore sp = new Semaphore(3); 14 for(int i=0;i<10;i++){ 15 Runnable runable = new Runnable() { 16 @Override 17 public void run() { 18 try{ 19 //取得sq许可 20 sp.acquire(); 21 }catch (Exception e) { 22 e.printStackTrace(); 23 } 24 System.out.println(Thread.currentThread().getName()+"进入"); 25 System.out.println("当前还有"+(3-sp.availablePermits())+"并发"); 26 //释放许可 27 sp.release(); 28 } 29 }; 30 service.execute(runable); 31 32 } 33 service.shutdown(); 34 } 35 36 }
Barrier
在实际应用中,有时候需要多个线程同时工作以完成同一件事情,而且在完成过程中,往往会等所有线程都到达某一个阶段后再统一执行。这是CyclicBarrier很有用。它允许一组线程互相等待,直到到达某个公共屏障点。在涉及一组固定大小的线程的程序中,这些线程必须不时地互相等待,此时CyclicBarrier很有用。因为该barrier在释放等待线程后可以重用,所以称它为循环的barrier.它重要的属性就是参与者个数,另外最重要的方法是await(),当所有线程都调用了await()后,就表示这些线程可以继续执行,否则就会等待。CyclicBarrier支持一个可选 的Runnable命令,在一组线程中的最后一个线程到达之后(但在释放所有线程之前),该命令只在每个屏障点运行一次。若在继续所有参与线程之前更新共享状态,此屏障操作有用。
CyclicBarrier:表示大家彼此等待,大家集合好后才开始出发,就是一定数组的线程在一些集合点汇合,只有所有线程都执行到后才开始下一步。
CyclicBarrier cb = new CyclicBarrier(个数3);
cb.await();
cb.await(); cb.getNumberWaiting();
cb.await();
1 public class CyclicBarrierTest { 2 3 public static void main(String[] args) throws IOException, InterruptedException { 4 CyclicBarrier barrier = new CyclicBarrier(3); 5 6 ExecutorService executor = Executors.newFixedThreadPool(3); 7 executor.submit(new Thread(new Runner(barrier, "1号选手"))); 8 executor.submit(new Thread(new Runner(barrier, "2号选手"))); 9 executor.submit(new Thread(new Runner(barrier, "3号选手"))); 10 11 executor.shutdown(); 12 } 13 } 14 15 class Runner implements Runnable { 16 // 一个同步辅助类,它允许一组线程互相等待,直到到达某个公共屏障点 (common barrier point) 17 private CyclicBarrier barrier; 18 19 private String name; 20 21 public Runner(CyclicBarrier barrier, String name) { 22 super(); 23 this.barrier = barrier; 24 this.name = name; 25 } 26 27 @Override 28 public void run() { 29 try { 30 Thread.sleep(1000 * (new Random()).nextInt(8)); 31 System.out.println(name + " 准备好了..."); 32 // barrier的await方法,在所有参与者都已经在此 barrier 上调用 await 方法之前,将一直等待。 33 barrier.await(); 34 } catch (InterruptedException e) { 35 e.printStackTrace(); 36 } catch (BrokenBarrierException e) { 37 e.printStackTrace(); 38 } 39 System.out.println(name + " 起跑!"); 40 } 41 }
CountDownLatch
在完成一组正在其他线程 中执行的操作之前,它允许一个或多个线程一直等待。用给定的数字作为计数器初始化CountDownLatch,一个线程调用await()方法后,在当前计数到达零之前,会一直受阻塞。其它线程调用countDown()方法,会使计数器减一,所以,计数器的值 为0后,会释放所有等待的线程。其它后续的await()调用都将立即返回。这种现象只出现一次,因为计数无法被重置。如果需要重置计数,请考虑使用CyclicBarrier.
CountDownLatch作为一个通用同步工具,有很多用途。使用1初始化的CountDownLatch用作一个简单的开、关锁存器,或入口:在通过调用countDown()的线程打开入口前,所有调用await()的线程都一直在入口处等待。用N初始化的CountDownLatch可以使一个线程在N个线程完成某项操作之前一直等待,或者使其在某项操作完成N次之前一直等待。
CountDownLatch:犹如倒计时器计数器,调用CountDownLatch对象的countDown
方法就将计算器减1,当计数到达0时,则所有等待者或单个等待者开始执行。
CountDownLatch cdOrder = new CountDownLatch(3);
Exchanger
java.util.concurrent.Exchanger提供了一个同步点,在这个同步点,一对线程可以交换数据。每个线程通过exchange()方法的入口提供数据给他的伙伴线程,并接收他的伙伴线程提供的数据,并返回。
Exchager用于实现两个人之间的数据交换,每个人在完成一定的事务后与对方交换数据,第一个先拿出数据的人将一直等待第二个人拿到数据到来时,才能彼此交换数据