CyclicBarrier 公共屏障点

来源:互联网 发布:嘉兴兼职淘宝美工 编辑:程序博客网 时间:2024/05/17 03:32

在实际应用中,有时候需要多个线程同时工作以完成同一件事情,而且在完成过程中,往往会等待其他线程都完成某一阶段后再执行,等所有线程都到达某一个阶段后再统一执行。

JDK:

一个同步辅助类,它允许一组线程互相等待,直到到达某个公共屏障点 (common barrier point)。在涉及一组固定大小的线程的程序中,这些线程必须不时地互相等待,此时 CyclicBarrier 很有用。因为该 barrier 在释放等待线程后可以重用,所以称它为循环 的 barrier。

 

CyclicBarrier 支持一个可选的 Runnable 命令,在一组线程中的最后一个线程到达之后(但在释放所有线程之前),该命令只在每个屏障点运行一次。若在继续所有参与线程之前更新共享状态,此屏障操作 很有用。

 

 

对于失败的同步尝试,CyclicBarrier 使用了一种快速失败的、要么全部要么全不 (all-or-none) 的破坏模式:如果因为中断、失败或者超时等原因,导致线程过早地离开了屏障点,那么其他所有线程(甚至是那些尚未从以前的 await() 中恢复的线程)也将通过 BrokenBarrierException(如果它们几乎同时被中断,则用 InterruptedException)以反常的方式离开。

JDK地址:

http://www.cjsdn.net/doc/jdk50/java/util/concurrent/CyclicBarrier.html#CyclicBarrier(int, java.lang.Runnable)

 

 

//构造方法摘要CyclicBarrier(int parties)           //创建一个新的 CyclicBarrier,它将在给定数量的参与者(线程)处于等待状态时启动,但它不会在每个 barrier 上执行预定义的操作。CyclicBarrier(int parties, Runnable barrierAction)           //创建一个新的 CyclicBarrier,它将在给定数量的参与者(线程)处于等待状态时启动,并在启动 barrier 时执行给定的屏障操作,该操作由最后一个进入 barrier 的线程执行。

 

Example1:

package tags;import java.util.Random;import java.util.concurrent.CyclicBarrier;/**  * CyclicBarrier调用CyclicBarrier.await()进入等待的线程数, * 当线程数达到了CyclicBarrier初始时规定的数目时,所有进入等待状态的线程被唤醒并继续。 * CyclicBarrier就象它名字的意思一样,可看成是个障碍, 所有的线程必须到齐后才能一起通过这个障碍。 * CyclicBarrier初始时还可带一个Runnable的参数,此Runnable任务在CyclicBarrier的数目达到后,所有其它线程被唤醒前被执行。 */public class CyclicBarrierTest {    public static class ComponentThread implements Runnable {        CyclicBarrier barrier;// 计数器        int ID;    // 组件标识        int[] array;    // 数据数组        // 构造方法        public ComponentThread(CyclicBarrier barrier, int[] array, int ID) {            this.barrier = barrier;            this.ID = ID;            this.array = array;        }        public void run() {            try {                array[ID] = new Random().nextInt(100);                System.out.println("Component " + ID + " generates: " + array[ID]);                System.out.println("Component " + ID + " sleep"); // 在这里等待Barrier处                barrier.await();                System.out.println("Component " + ID + " awaked");                // 计算数据数组中的当前值和后续值                int result = array[ID] + array[ID + 1];                System.out.println("Component " + ID + " result: " + result);            } catch (Exception ex) {            }        }    }    /**     * 测试CyclicBarrier的用法     */    public static void testCyclicBarrier() {        final int[] array = new int[3];        CyclicBarrier barrier = new CyclicBarrier(2, new Runnable() {            // 在所有线程都到达Barrier时执行            public void run() {                System.out.println("testCyclicBarrier run");                array[2] = array[0] + array[1];            }        });        // 启动线程        new Thread(new ComponentThread(barrier, array, 0)).start();        new Thread(new ComponentThread(barrier, array, 1)).start();            }    public static void main(String[] args) {        CyclicBarrierTest.testCyclicBarrier();    }}

结果:

Component 1 generates: 40

Component 0 generates: 8

Component 1 sleep

Component 0 sleep

testCyclicBarrier run

Component 0 awaked

Component 0 result: 48

Component 1 awaked

Component 1 result: 88

 

 

两个线程分别执行,互不影响 ,执行到barrier.await();时该线程进入等待状态,

当两个线程都执行到barrier.await();时,达到CyclicBarrier启动所需的阻塞线程数,进入到new CyclicBarrier(2, new Runnable()...)里面的方法, 执行完里面的方法后,等待的两个线程再次被唤醒,继续各自执行线程后面的语句。 

 

 

Example2:

 

比如有几个旅行团需要途经深圳、广州、韶关、长沙最后到达武汉。旅行团中有自驾游的,有徒步的,有乘坐旅游大巴的;这些旅行团同时出发,并且每到一个目的地,都要等待其他旅行团到达此地后再同时出发,直到都到达终点站武汉。
这时候CyclicBarrier就可以派上用场。CyclicBarrier最重要的属性就是参与者个数,另外最要方法是await()。当所有线程都调用了await()后,就表示这些线程都可以继续执行,否则就会等待。

 

package tags;import java.text.SimpleDateFormat;import java.util.Date;import java.util.concurrent.BrokenBarrierException;import java.util.concurrent.CyclicBarrier;import java.util.concurrent.ExecutorService;import java.util.concurrent.Executors;public class TestCyclicBarrier {// 徒步需要的时间: Shenzhen, Guangzhou, Shaoguanprivate static int[] timeWalk = { 5, 8, 15};// 自驾游private static int[] timeSelf = { 1, 3, 4};// 旅游大巴private static int[] timeBus = { 2, 4, 6};static String now() {SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss");return sdf.format(new Date()) + ": ";}static class Tour implements Runnable {private int[] times;private CyclicBarrier barrier;private String tourName;public Tour(CyclicBarrier barrier, String tourName, int[] times) {this.times = times;this.tourName = tourName;this.barrier = barrier;}public void run() {try {Thread.sleep(times[0] * 1000);  //使用 times岔开各组时间,使其显示不同的now()System.out.println(now() + tourName + " Reached Shenzhen");barrier.await();Thread.sleep(times[1] * 1000);System.out.println(now() + tourName + " Reached Guangzhou");barrier.await();Thread.sleep(times[2] * 1000);System.out.println(now() + tourName + " Reached Shaoguan");} catch (InterruptedException e) {} catch (BrokenBarrierException e) {}}}public static void main(String[] args) {// 三个旅行团CyclicBarrier barrier = new CyclicBarrier(3);ExecutorService exec = Executors.newFixedThreadPool(3);exec.submit(new Tour(barrier, "WalkTour", timeWalk));exec.submit(new Tour(barrier, "SelfTour", timeSelf));exec.submit(new Tour(barrier, "BusTour", timeBus));exec.shutdown();}}

  结果:

 

15:13:11: SelfTour Reached Shenzhen

15:13:12: BusTour Reached Shenzhen

15:13:15: WalkTour Reached Shenzhen

15:13:18: SelfTour Reached Guangzhou

15:13:19: BusTour Reached Guangzhou

15:13:23: WalkTour Reached Guangzhou

15:13:27: SelfTour Reached Shaoguan

15:13:29: BusTour Reached Shaoguan

15:13:38: WalkTour Reached Shaoguan

 

例子来源:http://conkeyn.iteye.com/blog/546280(测试时稍有修改)

 

 

 

 源码分析:

private final ReentrantLock lock = new ReentrantLock();    private final Condition trip = lock.newCondition();    /** 达到的数量 */    private final int parties;    /* 数量满足后执行的线程 */    private final Runnable barrierCommand;    /** The current generation */    private Generation generation = new Generation();private static class Generation {        boolean broken = false;    }public CyclicBarrier(int parties) {        this(parties, null);    }public CyclicBarrier(int parties, Runnable barrierAction) {        if (parties <= 0) throw new IllegalArgumentException();        this.parties = parties;        this.count = parties;        this.barrierCommand = barrierAction;    }

 

public int await() throws InterruptedException, BrokenBarrierException {        try {            return dowait(false, 0L);        } catch (TimeoutException toe) {            throw new Error(toe); // cannot happen;        }    }private int dowait(boolean timed, long nanos) throws InterruptedException,BrokenBarrierException,TimeoutException {    final ReentrantLock lock = this.lock;    lock.lock();    try {        final Generation g = generation;        if (g.broken) throw new BrokenBarrierException();        if (Thread.interrupted()) {            breakBarrier();            throw new InterruptedException();        }        int index = --count;        if (index == 0) { // index为0时,线程数量达到屏障数量,执行barrierCommand,并唤醒所有线程            boolean ranAction = false;            try {                final Runnable command = barrierCommand;                if (command != null) command.run();                ranAction = true;                nextGeneration();                return 0;            } finally {                if (!ranAction) breakBarrier();            }        }        // loop until tripped, broken, interrupted, or timed out        for (;;) {            try {                if (!timed)//若未达到屏障数量,每一个线程进入时都阻塞在这//此时会释放锁,以便下一线程进入trip.await();                else if (nanos > 0L) nanos = trip.awaitNanos(nanos);            } catch(InterruptedException ie) {                if (g == generation && !g.broken) {                    breakBarrier();                    throw ie;                } else {                    // We're about to finish waiting even if we had not                    // been interrupted, so this interrupt is deemed to                    // "belong" to subsequent execution.                    Thread.currentThread().interrupt();                }            }            if (g.broken) throw new BrokenBarrierException();            if (g != generation) return index;            if (timed && nanos <= 0L) {                breakBarrier();                throw new TimeoutException();            }        }    } finally {        lock.unlock();    }}

 

/**     * Sets current barrier generation as broken and wakes up everyone.     * Called only while holding lock.     */    private void breakBarrier() {        generation.broken = true;count = parties;        trip.signalAll();    }private void nextGeneration() {        // 唤醒所有线程        trip.signalAll();        // set up next generation// 重置屏障数量,使其可循环使用        count = parties;        generation = new Generation();    }

 

其实就是使用一个重入锁,一个Condition实现。

 

 

 

 

 

 

0 0
原创粉丝点击