【Java并发】闭锁 CountDownLatch 和 回环栅栏CyclicBarrier

这两个目的都是为了等待所有线程任务执行完之后再执行其他任务。

与CyclicBarrier不同的是,CountDownLatch等待的是一组线程执行完之后,才开始执行其他任务,而这一组线程之间相互不影响;而CyclicBarrier则是等待一组线程全部执行完之后才执行其他任务,这里等待是只一组线程之间的相互等待,线程之间有相互影响。并且CyclicBarrier是可复用的,而CountDownLatch则不能,同时CyclicBarrier是递增的方式,而CountDownLatch是递减的方式计数。

CountDownLatch例子:

    public class CountDownLatchTest {
        static Logger logger = Logger.getLogger("console");
        public static void main(String[] args) throws InterruptedException {
            int N = 5;
            CountDownLatch latch = new CountDownLatch(N);
            for (int i=0;i<N;i++){
                new Thread(new MyThread(latch),"线程"+i).start();
            }
            System.out.println(Thread.currentThread().getName()+"等待其他线程执行");
            latch.await();
            System.out.println(Thread.currentThread().getName()+":线程执行完毕,执行其他任务");
        }
        static class MyThread implements Runnable{
            private CountDownLatch latch;
            public MyThread(CountDownLatch latch){
                this.latch = latch;
            }
            @Override
            public void run() {
                try {
                    System.out.println(Thread.currentThread().getName()+"开始执行");
                    Thread.sleep(new Random().nextInt(1000));
                    latch.countDown();
                    System.out.println(Thread.currentThread().getName()+"执行结束");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
    //输出
    main等待其他线程执行
    线程0开始执行
    线程3开始执行
    线程1开始执行
    线程4开始执行
    线程2开始执行
    线程3执行结束
    线程1执行结束
    线程0执行结束
    线程4执行结束
    线程2执行结束
    main:线程执行完毕,执行其他任务

CyclicBarrie例子:

    public class CyclicBarrieTest {
        public static void main(String[] args) throws InterruptedException, BrokenBarrierException {
            int N = 5;
            System.out.println(Thread.currentThread().getName()+"开始执行");
            CyclicBarrier barrier = new CyclicBarrier(N, new Runnable() {
                @Override
                public void run() {
                    System.out.println(Thread.currentThread().getName()+":线程执行完,执行其他任务");
                }
            });
            for (int i=0;i<N;i++){
                new Thread(new CyclicBarrieTest.MyThread(barrier),"线程"+i).start();
            }
            System.out.println(Thread.currentThread().getName()+"执行结束");

            /////复用///
            Thread.sleep(2000);
            System.out.println("--------CyclicBarrieTest复用-------");
            for (int i=0;i<N;i++){
                new Thread(new CyclicBarrieTest.MyThread(barrier),"线程"+i).start();
            }
        }
        static class MyThread implements Runnable{
            private CyclicBarrier barrier;
            public MyThread(CyclicBarrier barrier){
                this.barrier = barrier;
            }
            @Override
            public void run() {
                try {
                    System.out.println(Thread.currentThread().getName()+"开始执行");
                    Thread.sleep(new Random().nextInt(1000));
                    System.out.println(Thread.currentThread().getName()+"执行结束");
                    barrier.await();
                } catch (InterruptedException | BrokenBarrierException e) {
                    e.printStackTrace();
                }
            }
        }
    }
    //输出
    main开始执行
    main执行结束
    线程0开始执行
    线程1开始执行
    线程2开始执行
    线程4开始执行
    线程3开始执行
    线程2执行结束
    线程0执行结束
    线程4执行结束
    线程1执行结束
    线程3执行结束
    线程3:线程执行完,执行其他任务
    --------CyclicBarrieTest复用-------
    线程0开始执行
    线程1开始执行
    线程2开始执行
    线程3开始执行
    线程4开始执行
    线程0执行结束
    线程4执行结束
    线程3执行结束
    线程1执行结束
    线程2执行结束
    线程2:线程执行完,执行其他任务

可以看到CyclicBarrie是可复用的,在达到最大计数只后会自动重新计数。并且CyclicBarrieTest的构造方法的第二个参数线程是用最后一个到达的线程去执行的。


使用场景:

CountDownLatch:适合在主线程需要等待子线程执行完毕后在执行或者需要等待某些资源的时候。

CyclicBarrier:适合需要线程相互等待对方完成时,比如多线程计算,在对计算结果进行汇总。

原文地址:https://www.cnblogs.com/cnsec/p/13286690.html