zoukankan      html  css  js  c++  java
  • Java 并发同步器之CountDownLatch、CyclicBarrier

    一、简介                                                                                                 

       1、CountDownLatch是一个同步计数器,构造时传入int参数,该参数就是计数器的初始值,每调用一次countDown()方法,计数器减1,计数器大于0 时,await()方法会阻塞程序的执行。 CountDownLatch可以看作是一个倒计数的锁存器,当计数减至0时触发特定的事件。利用这种特性,可以让主线程等待子线程的结束。

    CountDownLatch最重要的方法是countDown()和await(),前者主要是倒数一次,后者是等待倒数到0,如果没有到达0,就只有阻塞等待了。

       2、CyclicBarrier也是一个同步辅助类,它允许一组线程互相等待,直到到达某个公共屏障点 (common barrier point)。wait() 方法每被调用一次,计数便会减少1,并阻塞住当前线程。当计数减至0时,阻塞解除,所有在此 CyclicBarrier上面阻塞的线程开始运行。在这之后,如果再次调用 await() 方法,计数就又会变成 N-1,新一轮重新开始,所以称它为循环的 barrier。

    二、使用场景                                                                                            

       1、CountDownLatch可用于一组线程和另外一组线程的协作。比如有一个任务想要往下执行,但必须要等到其他的任务执行完毕后才可以继续往下执行。假如我们这个想要继续往下执行的任务调用一个CountDownLatch对象的await()方法,其他的任务执行完自己的任务后调用同一个CountDownLatch对象上的countDown()方法,那这个调用await()方法的任务将一直阻塞等待,直到这个CountDownLatch对象的计数值减到0为止。

       2、CyclicBarrier需要所有的子任务都完成时,才执行主任务,这个时候就可以选择使用CyclicBarrier。

    CountDownLatch 适用于一组线程和另一个主线程之间的工作协作。一个主线程等待一组工作线程的任务完毕才继续它的执行是使用 CountDownLatch 的主要场景;CyclicBarrier 用于一组或几组线程,比如一组线程需要在一个时间点上达成一致,例如同时开始一个工作。另外,CyclicBarrier 的循环特性和构造函数所接受的 Runnable 参数也是 CountDownLatch 所不具备的。

    三、常用方法                                                                                            

    CountDownLatch 
    1、public void countDown()
    递减锁存器的计数,如果计数到达零,则释放所有等待的线程。如果当前计数大于零,则将计数减少。如果新的计数为零,出于线程调度目的,将重新启用所有的等待线程。
    如果当前计数等于零,则不发生任何操作。
    
    2、public boolean await(long timeout, TimeUnit unit) throws InterruptedException
    使当前线程在锁存器倒计数至零之前一直等待,除非线程被中断或超出了指定的等待时间。如果当前计数为零,则此方法立刻返回 true 值。
    如果当前计数大于零,则出于线程调度目的,将禁用当前线程,且在发生以下三种情况之一前,该线程将一直处于休眠状态:
    由于调用 countDown() 方法,计数到达零;或者
    其他某个线程中断当前线程;或者
    已超出指定的等待时间。
    如果计数到达零,则该方法返回 true 值。
    如果当前线程:
    在进入此方法时已经设置了该线程的中断状态;或者
    在等待时被中断,
    则抛出 InterruptedException,并且清除当前线程的已中断状态。如果超出了指定的等待时间,则返回值为 false。如果该时间小于等于零,则此方法根本不会等待。
     
    参数:
    timeout - 要等待的最长时间
    unit - timeout 参数的时间单位。
    返回:
    如果计数到达零,则返回 true;如果在计数到达零之前超过了等待时间,则返回 false
    抛出:
    InterruptedException - 如果当前线程在等待时被中断
    CyclicBarrier 
    public int await() throws InterruptedException, BrokenBarrierException
    在所有参与者都已经在此 barrier 上调用 await方法之前,将一直等待。如果当前线程不是将到达的最后一个线程,出于调度目的,将禁用它,且在发生以下情况之一前,该线程将一直处于休眠状态:
    最后一个线程到达;或者
    其他某个线程中断当前线程;或者
    其他某个线程中断另一个等待线程;或者
    其他某个线程在等待 barrier 时超时;或者
    其他某个线程在此 barrier 上调用 reset()。
    如果当前线程:
    在进入此方法时已经设置了该线程的中断状态;或者
    在等待时被中断
    则抛出 InterruptedException,并且清除当前线程的已中断状态。如果在线程处于等待状态时 barrier 被 reset(),或者在调用 await 时 barrier 被损坏,抑或任意一个线程正处于等待状态,则抛出 BrokenBarrierException 异常。
    如果任何线程在等待时被 中断,则其他所有等待线程都将抛出 BrokenBarrierException 异常,并将 barrier 置于损坏状态。
    如果当前线程是最后一个将要到达的线程,并且构造方法中提供了一个非空的屏障操作,则在允许其他线程继续运行之前,当前线程将运行该操作。如果在执行屏障操作过程中发生异常,则该异常将传播到当前线程中,并将 barrier 置于损坏状态。
     
    返回:
    到达的当前线程的索引,其中,索引 getParties() - 1 指示将到达的第一个线程,零指示最后一个到达的线程
    抛出:
    InterruptedException - 如果当前线程在等待时被中断
    BrokenBarrierException - 如果另一个 线程在当前线程等待时被中断或超时,或者重置了 barrier,或者在调用 await 时 barrier 被损坏,抑或由于异常而导致屏障操作(如果存在)失败。

    四、示例                                                                                             

       我们看下JDK文档中的示例

    1、CountDownLatch 

    我们可以想象下赛车上的情景赛车的维修过程,只有 startSignal命令下达之后,维修工才开始干活,只有等所有工人完成工作之后,赛车才能继续。

    class Driver { // ...
        void main() throws InterruptedException {
            CountDownLatch startSignal = new CountDownLatch(1);
            CountDownLatch doneSignal = new CountDownLatch(N);
    
            for (int i = 0; i < N; ++i) // create and start threads
                new Thread(new Worker(startSignal, doneSignal)).start();
    
            doSomethingElse();            // don't let run yet
            startSignal.countDown();      // let all threads proceed
            doSomethingElse();
            doneSignal.await();           // wait for all to finish
        }
    }
    
    class Worker implements Runnable {
        private final CountDownLatch startSignal;
        private final CountDownLatch doneSignal;
        Worker(CountDownLatch startSignal, CountDownLatch doneSignal) {
            this.startSignal = startSignal;
            this.doneSignal = doneSignal;
        }
        public void run() {
            try {
                startSignal.await();
                doWork();
                doneSignal.countDown();
            } catch (InterruptedException ex) {} // return;
        }
    
        void doWork() { ... }
    }

    2、CyclicBarrier 

    class Solver {
        final int N;
        final float[][] data;
        final CyclicBarrier barrier;
    
        class Worker implements Runnable {
            int myRow;
            Worker(int row) { myRow = row; }
            public void run() {
                while (!done()) {
                    processRow(myRow);
    
                    try {
                        barrier.await();
                    } catch (InterruptedException ex) {
                        return;
                    } catch (BrokenBarrierException ex) {
                        return;
                    }
                }
            }
        }
    
        public Solver(float[][] matrix) {
            data = matrix;
            N = matrix.length;
            barrier = new CyclicBarrier(N, new Runnable() {
                    public void run() {
                        mergeRows(...);
                    }
                });
            for (int i = 0; i < N; ++i)
                new Thread(new Worker(i)).start();
    
            waitUntilDone();
        }
    }

     五、总结                                                                                              

       CountDownLatch 是能使一组线程等另一组线程都跑完了再继续跑;

     CyclicBarrier 能够使一组线程在一个时间点上达到同步,可以是一起开始执行全部任务或者一部分任务。同时,它是可以循环使用的。

    参考:http://my.oschina.net/lifany/blog/207995?p=2#comments 

    由于本人经验有限,文章中难免会有错误,请浏览文章的您指正或有不同的观点共同探讨!

  • 相关阅读:
    关于celery踩坑
    关于git的分批提交pull requests流程
    SymGAN—Exploiting Images for Video Recognition: Heterogeneous Feature Augmentation via Symmetric Adversarial Learning学习笔记
    AFN—Larger Norm More Transferable: An Adaptive Feature Norm Approach for Unsupervised Domain Adaptation学习笔记
    Learning to Transfer Examples for Partial Domain Adaptation学习笔记
    Partial Adversarial Domain Adaptation学习笔记
    Partial Transfer Learning with Selective Adversarial Networks学习笔记
    Importance Weighted Adversarial Nets for Partial Domain Adaptation学习笔记
    Exploiting Images for Video Recognition with Hierarchical Generative Adversarial Networks学习笔记
    improved open set domain adaptation with backpropagation 学习笔记
  • 原文地址:https://www.cnblogs.com/exceptioneye/p/4824579.html
Copyright © 2011-2022 走看看