zoukankan      html  css  js  c++  java
  • CountDownLatch与CyclicBarrier

    CountDownLatch

    倒数计数器 一个线程等待其他所有线程

     一.CountDownLatch用法

    CountDownLatch类位于java.util.concurrent包下,利用它可以实现类似计数器的功能。比如有一个任务A,它要等待其他4个任务执行完毕之后才能执行,此时就可以利用CountDownLatch来实现这种功能了。

    • await() throws InterruptedException:调用该方法的线程等到构造方法传入的N减到0的时候,才能继续往下执行;
    • await(long timeout, TimeUnit unit):与上面的await方法功能一致,只不过这里有了时间限制,调用该方法的线程等到指定的timeout时间后,不管N是否减至为0,都会继续往下执行;
    • countDown():使CountDownLatch初始值N减1;
    • long getCount():获取当前CountDownLatch维护的值;
    package ThreadLearn;
    import java.util.concurrent.CountDownLatch;
    public class CountDownLatchTest {
        
        /***
         * 
         * public void await() throws InterruptedException { };   
         * //调用await()方法的线程会被挂起,它会等待直到count值为0才继续执行
         * public boolean await(long timeout, TimeUnit unit) throws InterruptedException { };
         * //和await()类似,只不过等待一定的时间后count值还没变为0的话就会继续执行
         *  public void countDown() { };  //将count值减1
         * 
         * */    
             public static void main(String[] args) {   
                 final CountDownLatch latch = new CountDownLatch(2);
         
                 new Thread(){
                     public void run() {
                         try {
                             System.out.println("子线程"+Thread.currentThread().getName()+"正在执行");
                            Thread.sleep(3000);
                            System.out.println("子线程"+Thread.currentThread().getName()+"执行完毕");
                            latch.countDown();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                     };
                 }.start();
         
                 new Thread(){
                     public void run() {
                         try {
                             System.out.println("子线程"+Thread.currentThread().getName()+"正在执行");
                             Thread.sleep(3000);
                             System.out.println("子线程"+Thread.currentThread().getName()+"执行完毕");
                             latch.countDown();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                     };
                 }.start();
         
                 try {
                    System.out.println("等待2个子线程执行完毕...");
                    latch.await();
                    System.out.println("2个子线程已经执行完毕");
                    System.out.println("继续执行主线程");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
             }
        }

    Cyclicbarrier 循环栅栏

    栅栏类似于闭锁,它能阻塞一组线程直到某个事件的发生。栅栏与闭锁的关键区别在于,所有的线程必须同时到达栅栏位置,才能继续执行。闭锁用于等待事件,而栅栏用于等待其他线程。

    CyclicBarrier可以使一定数量的线程反复地在栅栏位置处汇集。当线程到达栅栏位置时将调用await方法,这个方法将阻塞直到所有线程都到达栅栏位置。如果所有线程都到达栅栏位置,那么栅栏将打开,此时所有的线程都将被释放,而栅栏将被重置以便下次使用。

    CountDownLatch只有一次计数 ,

    而Cyclicbarrier多次循环计数

    //其参数表示屏障拦截的线程数量,每个线程使用await()方法告诉CyclicBarrier我已经到达了屏障,然后当前线程被阻塞。
    await() throws InterruptedException, BrokenBarrierException
    //与上面的await方法功能基本一致,只不过这里有超时限制,阻塞等待直至到达超时时间为止
    await(long timeout, TimeUnit unit) throws InterruptedException, BrokenBarrierException, TimeoutException
    //获取当前有多少个线程阻塞等待在临界点上
    int getNumberWaiting()
    //用于查询阻塞等待的线程是否被中断
    boolean isBroken()
    //将屏障重置为初始状态。如果当前有线程正在临界点等待的话,将抛出
    BrokenBarrierException。 void reset()
    public CyclicBarrier(int parties, Runnable barrierAction)//用于线程到达屏障时,优先执行barrierAction,方便处理更复杂的业务场景。
    dowait(boolean, long)方法,它也是CyclicBarrier的核心方法,该方法定义如下:
    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()) {
                // 将损坏状态设置为true
                // 并通知其他阻塞在此栅栏上的线程
                breakBarrier();
                throw new InterruptedException();
            }
     
            // 获取下标
            int index = --count;
            // 如果是 0,说明最后一个线程调用了该方法
            if (index == 0) {  // tripped
                boolean ranAction = false;
                try {
                    final Runnable command = barrierCommand;
                    // 执行栅栏任务
                    if (command != null)
                        command.run();
                    ranAction = true;
                    // 更新一代,将count重置,将generation重置
                    // 唤醒之前等待的线程
                    nextGeneration();
                    return 0;
                } finally {
                    // 如果执行栅栏任务的时候失败了,就将损坏状态设置为true
                    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 {
                        // 上面条件不满足,说明这个线程不是这代的
                        // 就不会影响当前这代栅栏的执行,所以,就打个中断标记
                        Thread.currentThread().interrupt();
                    }
                }
     
                // 当有任何一个线程中断了,就会调用breakBarrier方法
                // 就会唤醒其他的线程,其他线程醒来后,也要抛出异常
                if (g.broken)
                    throw new BrokenBarrierException();
     
                // g != generation表示正常换代了,返回当前线程所在栅栏的下标
                // 如果 g == generation,说明还没有换代,那为什么会醒了?
                // 因为一个线程可以使用多个栅栏,当别的栅栏唤醒了这个线程,就会走到这里,所以需要判断是否是当前代。
                // 正是因为这个原因,才需要generation来保证正确。
                if (g != generation)
                    return index;
                
                // 如果有时间限制,且时间小于等于0,销毁栅栏并抛出异常
                if (timed && nanos <= 0L) {
                    breakBarrier();
                    throw new TimeoutException();
                }
            }
        } finally {
            // 释放独占锁
            lock.unlock();
        }
    }

    dowait(boolean, long)方法的主要逻辑处理比较简单,如果该线程不是最后一个调用await方法的线程,则它会一直处于等待状态,除非发生以下情况:

    最后一个线程到达,即index == 0
    某个参与线程等待超时
    某个参与线程被中断
    调用了CyclicBarrier的reset()方法。该方法会将屏障重置为初始状态。


    CountDownLatch与CyclicBarrier

    • CountDownLatch一般用于某个线程A等待若干个其他线程执行完任务之后,它才执行;而CyclicBarrier一般用于一组线程互相等待至某个状态,然后这一组线程再同时执行;CountDownLatch强调一个线程等多个线程完成某件事情。CyclicBarrier是多个线程互等,等大家都完成,再携手共进。
    • 调用CountDownLatch的countDown方法后,当前线程并不会阻塞,会继续往下执行;而调用CyclicBarrier的await方法,会阻塞当前线程,直到CyclicBarrier指定的线程全部都到达了指定点的时候,才能继续往下执行;
    • CountDownLatch方法比较少,操作比较简单,而CyclicBarrier提供的方法更多,比如能够通过getNumberWaiting(),isBroken()这些方法获取当前多个线程的状态,并且CyclicBarrier的构造方法可以传入barrierAction,指定当所有线程都到达时执行的业务功能;
    • CountDownLatch是不能复用的,而CyclicBarrier是可以复用的。
     

    链接:https://juejin.im/post/5aeec3ebf265da0ba76fa327

    链接:https://blog.csdn.net/qq_38293564/article/details/80558157

  • 相关阅读:
    依赖反转Ioc和unity,autofac,castle框架教程及比较
    webform非表单提交时防xss攻击
    tfs分支操作
    防火墙入站出站规则配置
    前端流程图jsplumb学习笔记
    Js闭包学习笔记
    word中加入endnote
    Rest概念学习
    DRF的版本、认证、权限
    博客园自动生成目录
  • 原文地址:https://www.cnblogs.com/dingpeng9055/p/11272817.html
Copyright © 2011-2022 走看看