CountDownLatch和CyclicBarrier的区别

CountDownLatch是一个同步的辅助类,允许一个或多个线程,等待其他一组线程完成操作,再继续执行。

CyclicBarrier是一个同步的辅助类,允许一组线程之间相互等待,达到一个共同点,再继续执行。

CountDownLatch的计数器无法被重置;CyclicBarrier的计数器可以被重置后使用,因此它被称为是循环的barrier。

他们都是:Synchronization  aid,把它翻译成同步辅助器,既然是辅助工具,怎么使用?哪些场景使用?

个人理解:把CountDownLatch理解成倒计时锁。

场景还原:一年级期末考试要开始了,监考老师发下去试卷,然后坐在讲台旁边玩着手机等待着学生答题,有的学生提前交了试卷,并约起打球了,等到最后一个学生交卷了,老师开始整理试卷,贴封条,下班。

补充场景:我们在玩LOL英雄联盟时会出现十个人不同加载状态,但是最后一个人由于各种原因始终加载不了100%,于是游戏系统自动等待所有玩家的状态都准备好,才展现游戏画面。

个人理解:把CyclicBarrier看成是个障碍,所有的线程必须到齐后才能一起通过这个障碍

场景还原:在军队里,士兵经常训练的项目有翻越高墙、双杠练习、四百米障碍、越野三公里跑等。将士兵分成多队进行比赛,每队的成员都必须共同完成一个关卡之后,才能一起前往下一个关卡,也就是说,同一个队伍中,先完成这一关的士兵,必须等待同队中其他士兵都完成了,才能一起进入到下一个比赛关卡。

1、CountDownLatch的原理和使用

CountDownLatch是通过“共享锁”实现的。

CountDownLatch的方法说明:

public CountDownLatch(int count)构造函数的作用是创建一个指定计数的锁存器。

public void countDown()方法的作用是递减锁存器的计数,如果计数为零,则释放所有等待的线程。如果当前计数大于零,则让计数递减。线程每调用一次该方法,计数就会减一。若当前计数已经等于0,则调用该方法不会发生任何事情。

public void await() throws InterruptedException方法的作用是使当前线程一直等待,直到锁存器计数为零,或者线程被中断。如果当前计数为零,则此方法立即返回。

调用await()方法的线程将一直处于等待状态,除非发生以下两种情况之一:

1.其他线程调用countDown()方法让锁存器的计数递减为0;

2.其他线程中断当前线程。

public boolean await(long timeout, TimeUnit unit) throws InterruptedException方法的作用是使当前线程一直等待,直到锁存器计数为零,或者线程被中断,或者已超出指定的等待时间。

调用await(timeout, unit)方法的线程将一直处于等待状态,除非发生以下三种情况之一:

1.其他线程调用countDown()方法让锁存器的计数递减为0;

2.其他线程中断当前线程;

3.超出指定的等待时间。

CountDownLatch的典型用法:

1、某一线程在开始运行前等待n个线程执行完毕。

初始化计数为n的CountDownLatch latch = new CountDownLatch(n)对象,每当一个线程执行完毕,就在当前线程中调用latch.countDown()方法,让计数器减1,当计数器的值变为0时,调用latch.await()方法的线程就会被唤醒。

一个典型应用场景就是启动一个服务时,主线程需要等待多个组件加载完毕,之后再继续执行。

2、实现多个线程开始执行任务的最大并行性。

注意是并行性,不是并发,强调的是多个线程在某一时刻同时开始执行。类似于赛跑,多个运动员在起点,等待发令枪响,然后同时开跑。做法是初始化一个计数为1的CountDownLatch latch = new CountDownLatch(1)对象,多个线程在开始执行任务前,首先调用latch.await()方法进行等待,当主线程调用latch.countDown()方法时,计数器变为0,多个线程同时被唤醒。

示例1:

package com.sort.study.thread;

import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class CountDownLatchTest {

    public static void main(String[] args) {
        CountDownLatch latch = new CountDownLatch(3);
        testCDL(latch);
        System.out.println("主线程等待子线程任务执行结束......");
        try {
            latch.await();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("======主线程结束======");
    }

    private static void testCDL(CountDownLatch latch){
        ExecutorService service = Executors.newFixedThreadPool(1);
        Runnable runnable = new Runnable() {
            @Override
            public void run() {
                // %n 表示换行
                System.out.printf("当前线程[%s]开始执行任务......%n",
                 Thread.currentThread().getName());
                waitTime(2000);
                System.out.printf("当前线程[%s]执行任务结束。%n",
		Thread.currentThread().getName());
                //可以在一个线程中调用多次countDown方法,调用的次数要大于等于计数器的个数,否则主线程将一直等待
                latch.countDown();
                latch.countDown();
                latch.countDown();
                latch.countDown();
            }
        };
        service.execute(runnable);
    }

    private static void waitTime(long time){
        try {
            Thread.sleep(time);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

}

输出结果:

主线程等待子线程任务执行结束......
当前线程[pool-1-thread-1]开始执行任务......
当前线程[pool-1-thread-1]执行任务结束。
====主线程结束====

示例2:

package com.sort.study.thread;

import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class CountDownLatchTest {

    public static void main(String[] args) {
        int count = 5;
        CountDownLatch latch = new CountDownLatch(count);
        testCDL(latch,count);
        System.out.println("监考老师等待所有学生答题结束......");
        try {
            //在主线程中调用CountDownLatch对象的await方法,让主线程等待
            latch.await();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("====所有学生答题结束,监考老师封装试卷====");
    }

    private static void testCDL(CountDownLatch latch, int count){
        ExecutorService service = Executors.newFixedThreadPool(count);
        for (int i=0;i<count;i++){
            //创建count个线程
            Runnable runnable = new Runnable() {
                @Override
                public void run() {
                    // %n 表示换行
                    System.out.printf("学生[%s]开始考试答题......%n",
                            Thread.currentThread().getName());
                    waitTime(2000);
                    System.out.printf("学生[%s]答题结束,提交试卷。%n",
                            Thread.currentThread().getName());
                //在线程中调用CountDownLatch对象的countDown方法,让计数器减一
                    latch.countDown();
                }
            };
            service.execute(runnable);
        }
    }

    private static void waitTime(long time){
        try {
            Thread.sleep(time);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

}

输出结果(下面是可能的一种输出结果):

学生[pool-1-thread-1]开始考试答题......
学生[pool-1-thread-4]开始考试答题......
学生[pool-1-thread-5]开始考试答题......
监考老师等待所有学生答题结束......
学生[pool-1-thread-3]开始考试答题......
学生[pool-1-thread-2]开始考试答题......
学生[pool-1-thread-1]答题结束,提交试卷。
学生[pool-1-thread-3]答题结束,提交试卷。
学生[pool-1-thread-4]答题结束,提交试卷。
学生[pool-1-thread-5]答题结束,提交试卷。
学生[pool-1-thread-2]答题结束,提交试卷。
====所有学生答题结束,监考老师封装试卷====

示例3:

package com.sort.study.thread;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Random;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class CountDownLatchTest {
    public static void main(String[] args) {
        CountDownLatchTest cdl = new CountDownLatchTest();
        cdl.runRace();
    }

    private void runRace(){
        int count = 3;
        //初始化计数器为1的CountDownLatch,表示裁判员发枪的latch
        CountDownLatch refereeLatch = new CountDownLatch(1);
        //初始化计数器为count的CountDownLatch,表示运动员的latch
        CountDownLatch athleteLatch = new CountDownLatch(count);
        //创建保存运动员信息的容器
        List<Runner> list = Collections.synchronizedList(new ArrayList<>());
        //让运动员等待裁判员发枪,准备开跑
        this.athleteRun(refereeLatch,athleteLatch,count,list);
        this.waitTime(1000);
        //让裁判员发枪,并等待统计成绩
        this.refereeTiming(refereeLatch,athleteLatch,list);
    }

    private void athleteRun(CountDownLatch refereeLatch, CountDownLatch athleteLatch, int num, List<Runner> list){
        ExecutorService service = Executors.newCachedThreadPool();
        for (int i=0;i<num;i++){
            Runnable runnable = new Runnable() {
                @Override
                public void run() {
                    System.out.printf("运动员[%s]等待裁判发枪开跑。%n",
                            Thread.currentThread().getName());
                    Runner runner = new Runner(Thread.currentThread().getName());
                    try {
                        //当前线程调用CountDownLatch对象的await方法进行等待
                        //等待裁判员发枪之后,运动员才开跑
                        refereeLatch.await();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.printf("运动员[%s]开始跑步......%n",
                            Thread.currentThread().getName());
                    Random random = new Random();
                    int time = random.nextInt(5) * 1000;
                    waitTime(time);
                    runner.setTime(time);
                    list.add(runner);
                    System.out.printf("运动员[%s]跑到终点。%n",
                            Thread.currentThread().getName());
                    //当前线程调用CountDownLatch对象的countDown方法让计数器减一
                    //每个运动员跑完之后都将计数器减一,将等待统计成绩的裁判唤醒
                    athleteLatch.countDown();
                }
            };
            //执行任务
            service.execute(runnable);
        }
        //关闭线程池
        service.shutdown();
    }

    private void refereeTiming(CountDownLatch refereeLatch, CountDownLatch athleteLatch, List<Runner> list){
        System.out.printf("裁判员[%s]开始准备发枪......%n",
                Thread.currentThread().getName());
        //调用countDown方法让计数器减一,裁判发枪让运动员开跑
        refereeLatch.countDown();
        System.out.printf("裁判员[%s]等待统计成绩......%n",
                Thread.currentThread().getName());
        try {
            //等待所有的运动员都跑完之后,裁判员再一起统计成绩
            athleteLatch.await();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.printf("===所有运动员已跑完,裁判员[%s]统计成绩结束===%n",
                Thread.currentThread().getName());
        list.forEach(r -> {
            System.out.println(String.format("====运动员[%s]跑步花费的时间是[%d]====",r.getName(),r.getTime()));
        });
    }

    private void waitTime(long time){
        long l = (long) (Math.random() * time);
        try {
            Thread.sleep(l);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    private static class Runner{
        private String name;
        private long time;

        public Runner() {
        }

        public Runner(String name) {
            this.name = name;
        }

        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }

        public long getTime() {
            return time;
        }

        public void setTime(long time) {
            this.time = time;
        }
    }

}

输出结果:

运动员[pool-1-thread-1]等待裁判发枪开跑。
运动员[pool-1-thread-3]等待裁判发枪开跑。
运动员[pool-1-thread-2]等待裁判发枪开跑。
裁判员[main]开始准备发枪......
裁判员[main]等待统计成绩......
运动员[pool-1-thread-2]开始跑步......
运动员[pool-1-thread-1]开始跑步......
运动员[pool-1-thread-3]开始跑步......
运动员[pool-1-thread-3]跑到终点。
运动员[pool-1-thread-1]跑到终点。
运动员[pool-1-thread-2]跑到终点。
===所有运动员已跑完,裁判员[main]统计成绩结束===
====运动员[pool-1-thread-3]跑步花费的时间是[1000]====
====运动员[pool-1-thread-1]跑步花费的时间是[2000]====
====运动员[pool-1-thread-2]跑步花费的时间是[4000]====

2、CyclicBarrier的原理和使用

现实生活中我们经常会遇到这样的情景,在进行某个活动前需要等待人全部都齐了才开始。例如吃饭时要等全家人都上座了才动筷子,旅游时要等全部人都到齐了才出发,比赛时要等运动员都上场后才开始。

在JUC包中为我们提供了一个同步工具类能够很好的模拟这类场景,它就是CyclicBarrier类。利用CyclicBarrier类可以实现一组线程相互等待,当所有线程都到达某个屏障点后再进行后续的操作。

CyclicBarrier字面意思是“可重复使用的栅栏”,CyclicBarrier 相比 CountDownLatch 来说,要简单很多,其源码没有什么高深的地方,它是 ReentrantLock 和 Condition 的组合使用。

如下示意图,CyclicBarrier和CountDownLatch是不是很像,只是CyclicBarrier可以有不止一个栅栏,因为它的栅栏(Barrier)可以重复使用(Cyclic)。

首先,CyclicBarrier 的源码实现和 CountDownLatch 大同小异,CountDownLatch 基于 AQS 的共享模式的使用,而 CyclicBarrier 基于 Condition 来实现的。

在CyclicBarrier类的内部有一个计数器,每个线程在到达屏障点的时候都会调用await方法将自己阻塞,此时计数器会减1,当计数器减为0的时候所有因调用await方法而被阻塞的线程将被唤醒。这就是实现一组线程相互等待的原理。下面看一下CyclicBarrier的源码,由源码可知,在CyclicBarrier内部维护了ReentrantLock和Condition,所以CyclicBarrier是基于ReentrantLock + Condition实现的。

public class CyclicBarrier {
    /**内部类Generation是用来处理循环使用的问题,维护了一个broker状态表示当前的栅栏是否失效。如果失效,可以重置栅栏的状态。当栅栏被打破时,就设置当前generation的broker为true表示失效,并唤醒所有等待的线程*/
    private static class Generation {
        boolean broken = false;
    }

    /** 用于守卫屏障入口的同步锁 */
    private final ReentrantLock lock = new ReentrantLock();
    /** 创建一个条件队列,让所有线程在同一个条件队列上等待,当最后一个线程到达时,调用signalAll方法唤醒所有等待的线程*/
    private final Condition trip = lock.newCondition();
    /** 每次拦截的线程数 */
    private final int parties;
    /**换代前执行的任务,当所有线程都到达屏障时,由最后一个到达的线程执行该任务*/
    private final Runnable barrierCommand;
    /**栅栏的当前代*/
    private Generation generation = new Generation();

    /**初始值与parties相同,随着每次await方法的调用而减1,直到减为0就将所有线程唤醒。在每一个新代或屏障被打破时,count都会被重置为parties的值。*/
    private int count;

    /**唤醒所有线程并开启下一个屏障代,仅在持有锁时被调用。*/
    private void nextGeneration() {
        // signal completion of last generation
        trip.signalAll();
        // set up next generation
        count = parties;
        generation = new Generation();
    }

    /**将当前屏障代的状态设置为已打破并唤醒所有线程,仅在持有锁时被调用。*/
    private void breakBarrier() {
        generation.broken = true;
        count = parties;
        trip.signalAll();
    }

    /**
     * 主要的屏障代码,包含各种策略。
     */
    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;
//如果屏障被绊倒(tripped),即被打破,则说明当前线程是最后一个到达屏障的线程
            if (index == 0) {  // tripped
                boolean ranAction = false;
                try {
                    final Runnable command = barrierCommand;
//在创建CyclicBarrier时,如果指定的Runnable不为空,则由当前线程(也是最后一个线程)执行该Runnable
                    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();
        }
    }

    /**
     * Creates a new {@code CyclicBarrier} that will trip when the
     * given number of parties (threads) are waiting upon it, and which
     * will execute the given barrier action when the barrier is tripped,
     * performed by the last thread entering the barrier.
创建一个新的CyclicBarrier循环屏障,当给定数量的线程都到达该屏障前等待时,该屏障将会被绊倒(trip),也就是该屏障会被打破,所有线程都被唤醒,越过屏障。当屏障被打破时,由最后一个进入屏障的线程执行指定的屏障操作barrierAction。
     *
     * @param parties the number of threads that must invoke {@link #await}
     *        before the barrier is tripped
     * @param barrierAction the command to execute when the barrier is
     *        tripped, or {@code null} if there is no action
     * @throws IllegalArgumentException if {@code parties} is less than 1
     */
    public CyclicBarrier(int parties, Runnable barrierAction) {
        if (parties <= 0) throw new IllegalArgumentException();
        this.parties = parties;
        this.count = parties;
        this.barrierCommand = barrierAction;
    }

    /**
     * Creates a new {@code CyclicBarrier} that will trip when the
     * given number of parties (threads) are waiting upon it, and
     * does not perform a predefined action when the barrier is tripped.
     *
     * @param parties the number of threads that must invoke {@link #await}
     *        before the barrier is tripped
     * @throws IllegalArgumentException if {@code parties} is less than 1
     */
    public CyclicBarrier(int parties) {
        this(parties, null);
    }

    /**
     * Returns the number of parties required to trip this barrier.
     *
     * @return the number of parties required to trip this barrier
     */
    public int getParties() {
        return parties;
    }

    /**一直等待,直到所有线程调用在这个屏障上的await方法
     * Waits until all {@linkplain #getParties parties} have invoked
     * {@code await} on this barrier.
     *
如果当前线程不是最后一个到达的线程,则出于线程调度目的,它将被禁用,并处于休眠状态(dormant),直到发生以下情况之一:
最后一个线程到达;或
其他线程中断当前线程;或
其他线程中断另一个等待的线程;或
其他线程在等待屏障时超时;或
其他一些线程在此屏障上调用重置。
     * <p>If the current thread is not the last to arrive then it is
     * disabled for thread scheduling purposes and lies dormant until
     * one of the following things happens:
     * <ul>
     * <li>The last thread arrives; or
     * <li>Some other thread {@linkplain Thread#interrupt interrupts}
     * the current thread; or
     * <li>Some other thread {@linkplain Thread#interrupt interrupts}
     * one of the other waiting threads; or
     * <li>Some other thread times out while waiting for barrier; or
     * <li>Some other thread invokes {@link #reset} on this barrier.
     * </ul>
     */
    public int await() throws InterruptedException, BrokenBarrierException {
        try {
            return dowait(false, 0L);
        } catch (TimeoutException toe) {
            throw new Error(toe); // cannot happen
        }
    }

    public int await(long timeout, TimeUnit unit)
        throws InterruptedException,
               BrokenBarrierException,
               TimeoutException {
        return dowait(true, unit.toNanos(timeout));
    }

    /**
     * Queries if this barrier is in a broken state.
     *
     * @return {@code true} if one or more parties broke out of this
     *         barrier due to interruption or timeout since
     *         construction or the last reset, or a barrier action
     *         failed due to an exception; {@code false} otherwise.
     */
    public boolean isBroken() {
        final ReentrantLock lock = this.lock;
        lock.lock();
        try {
            return generation.broken;
        } finally {
            lock.unlock();
        }
    }

    /**
     * Resets the barrier to its initial state.  If any parties are
     * currently waiting at the barrier, they will return with a
     * {@link BrokenBarrierException}. Note that resets <em>after</em>
     * a breakage has occurred for other reasons can be complicated to
     * carry out; threads need to re-synchronize in some other way,
     * and choose one to perform the reset.  It may be preferable to
     * instead create a new barrier for subsequent use.
     */
    public void reset() {
        final ReentrantLock lock = this.lock;
        lock.lock();
        try {
            breakBarrier();   // break the current generation
            nextGeneration(); // start a new generation
        } finally {
            lock.unlock();
        }
    }

    /**
     * Returns the number of parties currently waiting at the barrier.
     * This method is primarily useful for debugging and assertions.
     *
     * @return the number of parties currently blocked in {@link #await}
     */
    public int getNumberWaiting() {
        final ReentrantLock lock = this.lock;
        lock.lock();
        try {
            return parties - count;
        } finally {
            lock.unlock();
        }
    }
}

示例1:运动员跑步比赛时需要等待大家都准备好了才能一起开跑。

package com.sort.study.thread;

import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class CyclicBarrierTest {

    public static void main(String[] args) {
        int count = 3;
        CyclicBarrier barrier = new CyclicBarrier(count);
        CyclicBarrierTest cbt = new CyclicBarrierTest();
        cbt.runRace(count,barrier);
        System.out.println("=====主线程结束=====");
    }

    private void runRace(int count, CyclicBarrier barrier){
        ExecutorService service = Executors.newFixedThreadPool(count);
        for (int i=0;i<count;i++){
            Runnable runnable = new Runnable() {
                @Override
                public void run() {
                    System.out.printf("当前运动员[%s]已准备好,等待其他运动员准备......%n",Thread.currentThread().getName());
                    try {
                        /*除了最后一个线程调用await方法时不会被阻塞,其他线程调用await方法时都会被阻塞,而且最后一个线程调用await方法时,底层会在nextGeneration方法中调用signalAll方法唤醒其他所有线程*/
                        barrier.await();
                    } catch (InterruptedException | BrokenBarrierException e) {
                        e.printStackTrace();
                    }
                    System.out.printf("所有运动员都已准备好,当前运动员[%s]开始起跑。%n",Thread.currentThread().getName());
                    waitTime(5000);
                    System.out.printf("当前运动员[%s]已跑完。%n", Thread.currentThread().getName());
                }
            };
            service.execute(runnable);
        }
        //关闭线程池
        service.shutdown();
    }

    private void waitTime(long time){
        long l = (long) (Math.random() * time);
        try {
            Thread.sleep(l);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

}

输出结果:

=====主线程结束=====
当前运动员[pool-1-thread-1]已准备好,等待其他运动员准备......
当前运动员[pool-1-thread-2]已准备好,等待其他运动员准备......
当前运动员[pool-1-thread-3]已准备好,等待其他运动员准备......
所有运动员都已准备好,当前运动员[pool-1-thread-3]开始起跑。
所有运动员都已准备好,当前运动员[pool-1-thread-1]开始起跑。
所有运动员都已准备好,当前运动员[pool-1-thread-2]开始起跑。
当前运动员[pool-1-thread-2]已跑完。
当前运动员[pool-1-thread-3]已跑完。
当前运动员[pool-1-thread-1]已跑完。

示例2:

package com.sort.study.thread;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * 需求:四个队员需要通过三关才能闯关成功,在闯每一关时,每个人可以单独做自己的闯关任务,在到达每一关的关卡时必须等待所有队员一起才能到下一关
 */
public class CyclicBarrierTest2 {

    private static final List<BreakGateRunnable> LIST = new ArrayList<>();

    public static void main(String[] args) {
        CyclicBarrierTest2 cbt = new CyclicBarrierTest2();
        cbt.breakGate();
        System.out.println("====主线程结束====");
    }

    private void breakGate(){
        int count = 4;
        Runnable runnable = new BreakGateInfoRunnable();
        //最后一个线程在调用CyclicBarrier对象的await方法时,会调用此处runnable对象的run方法
        CyclicBarrier barrier = new CyclicBarrier(count, runnable);
        ExecutorService service = Executors.newFixedThreadPool(count);
        for (int i=0;i<count;i++){
            BreakGateRunnable gateRun = new BreakGateRunnable(barrier);
            LIST.add(gateRun);
            service.execute(gateRun);
        }
        service.shutdown();
    }

    static class BreakGateInfoRunnable implements Runnable{
        @Override
        public void run() {
            for (BreakGateRunnable run : CyclicBarrierTest2.LIST){
                System.out.println(run.getBreakGateInfo());
            }
            System.out.println("所有队员都已到达,准备进入下一关。");
            System.out.println("======================================");
        }
    }

}

class BreakGateRunnable implements Runnable{

    private String breakGateInfo;

    private final CyclicBarrier barrier;

    public BreakGateRunnable(CyclicBarrier barrier){
        this.barrier = barrier;
    }

    @Override
    public void run() {
        int i = 0;
        int gateNum = 3;
        while (i < gateNum){
            //模拟闯关耗时
            long time = (long) (Math.random() * 3000);
            waitTime(time);
            breakGateInfo = String.format("队员[%s]闯第[%d]关耗时[%d]。",
                    Thread.currentThread().getName(),(i+1),time);
            System.out.printf("当前队员[%s]闯完第[%d]关,等待其他队员一起过关。%n",
                    Thread.currentThread().getName(),(i+1));
            try {
                //等待所有队员一起过关
                barrier.await();
            } catch (InterruptedException e) {
                e.printStackTrace();
            } catch (BrokenBarrierException e) {
                e.printStackTrace();
            }
            i++;
        }
    }

    //获取闯关信息
    public String getBreakGateInfo(){
        return breakGateInfo;
    }

    private void waitTime(long time){
        try {
            Thread.sleep(time);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

输出结果:

====主线程结束====
当前队员[pool-1-thread-1]闯完第[1]关,等待其他队员一起过关。
当前队员[pool-1-thread-2]闯完第[1]关,等待其他队员一起过关。
当前队员[pool-1-thread-3]闯完第[1]关,等待其他队员一起过关。
当前队员[pool-1-thread-4]闯完第[1]关,等待其他队员一起过关。
队员[pool-1-thread-1]闯第[1]关耗时[442]。
队员[pool-1-thread-2]闯第[1]关耗时[1036]。
队员[pool-1-thread-3]闯第[1]关耗时[1468]。
队员[pool-1-thread-4]闯第[1]关耗时[2011]。
所有队员都已到达,准备进入下一关。
======================================
当前队员[pool-1-thread-3]闯完第[2]关,等待其他队员一起过关。
当前队员[pool-1-thread-1]闯完第[2]关,等待其他队员一起过关。
当前队员[pool-1-thread-4]闯完第[2]关,等待其他队员一起过关。
当前队员[pool-1-thread-2]闯完第[2]关,等待其他队员一起过关。
队员[pool-1-thread-1]闯第[2]关耗时[829]。
队员[pool-1-thread-2]闯第[2]关耗时[1820]。
队员[pool-1-thread-3]闯第[2]关耗时[7]。
队员[pool-1-thread-4]闯第[2]关耗时[1742]。
所有队员都已到达,准备进入下一关。
======================================
当前队员[pool-1-thread-3]闯完第[3]关,等待其他队员一起过关。
当前队员[pool-1-thread-1]闯完第[3]关,等待其他队员一起过关。
当前队员[pool-1-thread-2]闯完第[3]关,等待其他队员一起过关。
当前队员[pool-1-thread-4]闯完第[3]关,等待其他队员一起过关。
队员[pool-1-thread-1]闯第[3]关耗时[600]。
队员[pool-1-thread-2]闯第[3]关耗时[1667]。
队员[pool-1-thread-3]闯第[3]关耗时[57]。
队员[pool-1-thread-4]闯第[3]关耗时[2372]。
所有队员都已到达,准备进入下一关。
======================================

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值