Java并发编程之——同步器工具类(semaphores, barriers, CountDownLatch,exchangers)

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用于实现两个人之间的数据交换,每个人在完成一定的事务后与对方交换数据,第一个先拿出数据的人将一直等待第二个人拿到数据到来时,才能彼此交换数据

posted @ 2013-06-15 18:33  丁丁木木  阅读(602)  评论(0编辑  收藏  举报