zoukankan      html  css  js  c++  java
  • JUC(3)---CountDownLatch、CyclicBarrier和AQS

    CountDownLatch:

      可以让一个线程等待其他线程完成了各自的工作之后再执行。比如说一个切菜,一个人切肉,都准备完毕之后才能炒肉。

    构造方法

    public CountDownLatch(int count)  count等待的线程数量

    关键API

    countDown()   分线程执行完减少计数

    await()        主线程等待调用

    使用

    package com.nijunyang.concurrent;
    
    import java.util.concurrent.CountDownLatch;
    
    /**
     * Description:
     * Created by nijunyang on 2020/5/16 13:53
     */
    public class CountDownLatchTest{
    
        private CountDownLatch countDownLatch;
    
        public CountDownLatchTest(CountDownLatch countDownLatch) {
            this.countDownLatch = countDownLatch;
        }
    
        public static void main(String[] args) throws InterruptedException {
    
            CountDownLatch countDownLatch = new CountDownLatch(2);
            CountDownLatchTest countDownLatchTest = new CountDownLatchTest(countDownLatch);
    
            new Thread(()-> {
                try {
                    countDownLatchTest.method1();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            },"线程1").start();
            new Thread(()-> {
                try {
                    countDownLatchTest.method2();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            },"线程2").start();
    
            System.out.println("等待食材准备完毕...");
            countDownLatch.await();
            System.out.println("炒肉...");
    
    
    //        System.out.println("------第二次使用-----");
    //        new Thread(()-> {
    //            try {
    //                countDownLatchTest.method1();
    //            } catch (InterruptedException e) {
    //                e.printStackTrace();
    //            }
    //        },"线程1").start();
    //        new Thread(()-> {
    //            try {
    //                countDownLatchTest.method2();
    //            } catch (InterruptedException e) {
    //                e.printStackTrace();
    //            }
    //        },"线程2").start();
    //
    //        System.out.println("等待食材准备完毕...");
    //        countDownLatch.await();
    //        System.out.println("炒肉...");
    
        }
    
        private void method1() throws InterruptedException {
            Thread.sleep(5000L);
            System.out.println("切菜完毕...");
            countDownLatch.countDown();
        }
    
        private void method2() throws InterruptedException {
            Thread.sleep(10000L);
            System.out.println("切肉完毕...");
            countDownLatch.countDown();
        }
    }

    原理解析

    1.从构造方法进去我们可以看到又是一个熟悉的Sync内部类继承了AbstractQueuedSynchronizer,入参的数量被赋值到AbstractQueuedSynchronizer的state字段。

    2.await方法会去判断state是否等于0,如果不等于0,说明其他线程还没有执行完毕。就会执行doAcquireSharedInterruptibly这个方法,将当前这个调用await方法的线程入队阻塞。

    (调用链:await()-sync.acquireSharedInterruptibly-sync.tryAcquireShared-doAcquireSharedInterruptibly)

    3.countDown方法,每调一次就会将state的值减1,当扣减到0的时候去唤醒上面等待的主线程执行(调用链:countDown-sync.releaseShared-sync.tryReleaseShared-doReleaseShared(减到0才会执行这方法))

    CyclicBarrier

    篱栅,顾名思义有拦截作用。它可以让一组线程到达栅栏时被阻塞,直到最后一个线程到达,才放行通过。比如玩LOL,需要等待所有玩家进度条100%了,才能进入游戏

    构造方法

    CyclicBarrier(int parties)      parties:阻塞的线程数量

    CyclicBarrier(int parties, Runnable barrierAction)  parties:阻塞的线程数量  barrierAction:当最后一个线程到达是先执行这个任务,再去执行后面的流程。

    关键API:

    await()  到达栅栏点等待。调用次数要和入参数量一致,否则会一致阻塞的等待。

    使用

    package com.nijunyang.concurrent;
    
    import java.util.concurrent.CyclicBarrier;
    
    /**
     * @author: create by nijunyang
     * @date:2019/9/5
     */
    public class CyclicBarrierTest implements Runnable{
        private CyclicBarrier cyclicBarrier;
        public CyclicBarrierTest(CyclicBarrier cyclicBarrier) {
            this.cyclicBarrier = cyclicBarrier;
        }
        public void run() {
            try {
                System.out.println(Thread.currentThread().getName() + "进度条100%... ");
                cyclicBarrier.await();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        public static void main(String[] args) throws Exception {
            CyclicBarrier cyclicBarrier = new CyclicBarrier(11, new Runnable() {//11个是因为还有一个主线程也在等待
                        public void run() {
                            System.out.println("所有人进度条100%,准备开始游戏");
                        }
                    });
            for (int i = 0; i < 10; i++) {
                new Thread(new CyclicBarrierTest(cyclicBarrier), "线程" + i).start();
            }
            cyclicBarrier.await();
            Thread.sleep(300);
            System.out.println("开始游戏....");
        }
    
    }

    原理解析

    1.默认每个CyclicBarrier对象有一把锁ReentrantLock和Condition

    2.将构造方法的入参数量赋值到count字段中。后续都是在count字段上面进行操作。

    3.await的调用会将count的数量-1,如果扣减到0.则会先执行构造方法传入的任务(如果传了),并且重置计数器刷新栅栏,将许可数据重新赋值给count字段(可以重复使用),唤醒条件等待的线程

     

     4.如果扣减完成之后还没有到0.说明还有线程没有到达栅栏点。则进入条件队列阻塞等到,等到最后一个到达时候,才被唤醒

     两者比较

    CountDownLatch和CyclicBarrier,最终实现效果看起来都差不多,都是等待分支线程执行完毕,再往下执行。然后CyclicBarrier这个可以重复使用,因为会去刷新count的数量。CountDownLatch不会重新刷新state字段的值。当第二次await执行的时候一看state是0就直接放行了,所以一个CountDownLatch对象只能使用一次。

    原理上CountDownLatch是阻塞主线程,分支线线程执行完毕将state扣减到0了之后唤醒主线程去执行,CyclicBarrier则是所有线程到达栅栏点都会阻塞等待。直到后一个到达才唤醒所有的阻塞线程。

  • 相关阅读:
    轻松搭建Redis缓存高可用集群
    Redis集群主从配置
    启动Redis Cluster
    MyISAM 和 InnoDB 索引的区别
    数据库面试
    如何定位php程序访问慢
    Socket技术详解
    NGINX快速入门
    nginx 并发数问题思考:worker_connections,worker_processes与 max clients
    php-fpm运行原理
  • 原文地址:https://www.cnblogs.com/nijunyang/p/12901261.html
Copyright © 2011-2022 走看看