关于java:CountDownLatch别浪等人齐再团

58次阅读

共计 3027 个字符,预计需要花费 8 分钟才能阅读完成。

一入王者深似海,从此对象是路人。

哈喽观众老爷们你们好,在下战神吕布字奉先,明天给大家来一部吕布的教学视频!

咳咳,不对。大家好,我是磊哥,明天给大家来一篇 CountDownLatch 的文章。

在开始之前,先问大家一个十分业余的技术性问题:打团战最怕_____?

一道非常简单的送分题,如果答不对,那磊哥就要批评你了,哈哈。

可能有人会说:打团战最怕 猪队友 ,但比猪队友 更可怕的是打团战人不齐 啊兄弟,想想在打团时如果是 5V2 是怎么一幅画面,心痛到不敢想🤦🏻‍♀️。

等人齐再团

磊哥在儿子没有出世之前,也是资深的农药玩家,至于段位吗?别问!问就是青铜。尽管磊哥的段位不是很高,但根本的大局观还是有的,毕竟也是打过几年 Dota 和 LOL 的青铜玩家是吧?哈哈。

农药和其余 Moba 类游戏是一样的,想要取胜,必须要把握好每次团战,而每次团战的关键在于 等人齐了再开团,是吧?而这个思维正好和咱们要讲得 CountDownLatch 的思维是统一的,咱们来看看是怎么回事吧。

吾有上将“CountDownLatch”

设想一下这样一个场景,当咱们须要期待某些线程执行完之后,再执行主线程的代码,要怎么实现?

可能有人会说,简略,用 join() 办法期待线程执行实现之后再执行主线程就行了,实现代码是这样的:

// 创立线程 1
Thread t1 = new Thread(new Runnable() {
    @Override
    public void run() {// do something}
});
t1.start();

// 创立线程 2
Thread t2 = new Thread(new Runnable() {
    @Override
    public void run() {// do something}
});
t2.start();

// 期待线程 1 和线程 2 执行完
t1.join();
t2.join();

当然,如果应用的是 Thread 来执行工作,那这种写法也是可行的。然而 实在的(编码)环境中咱们是不会应用 Thread 来执行多任务的,而是会应用线程池来执行多任务,这样能够防止线程反复启动和销毁所带来的性能开销,实现代码如下:

// 创立固定线程数的线程池
ExecutorService executorService = Executors.newFixedThreadPool(2);
// 工作一
executorService.submit(new Runnable() {
    @Override
    public void run() {// do something}
});
// 工作二
executorService.submit(new Runnable() {
    @Override
    public void run() {// do something}
});

那么 这时候问题来了,线程池是没有 join() 办法的,那要怎么实现期待呢?

这个时候就要派出我方大将“CountDownLatch”啦。

吾有上将潘凤,可斩华雄 … 出场数秒,潘凤 …“卒”。

等等导演,我感觉剧情应该是这样的 …

CountDownLatch 应用

为了实现期待所有线程池执行完之后再执行主线程的逻辑,我决定应用 AQS(AbstractQueuedSynchronizer,形象同步框架)下的驰名类 CountDownLatch 来实现此性能,具体的实现代码如下:

public static void main(String[] args) throws InterruptedException {
    // 创立 CountDownLatch
    CountDownLatch countDownLatch = new CountDownLatch(2);

    // 创立固定线程数的线程池
    ExecutorService executorService = Executors.newFixedThreadPool(2);
    // 工作一
    executorService.submit(new Runnable() {
        @Override
        public void run() {
            // do something
            try {
                // 让此工作执行 1.2s
                Thread.sleep(1200);
            } catch (InterruptedException e) {e.printStackTrace();
            }
            System.out.println("我是工作一");
            countDownLatch.countDown();}
    });
    // 工作二
    executorService.submit(new Runnable() {
        @Override
        public void run() {
            // do something
            try {
                // 让此工作执行 1.2s
                Thread.sleep(1000);
            } catch (InterruptedException e) {e.printStackTrace();
            }
            System.out.println("我是工作二");
            countDownLatch.countDown();}
    });
    
    // 期待工作执行实现
    countDownLatch.await();
    System.out.println("程序执行实现~");
}

以上程序执行后果如下:

从上述后果能够看出,主线程的执行是期待工作一和工作二都执行实现之后才执行的。

CountDownLatch 实现原理

CountDownLatch 中 count down 是倒数的意思,latch 则是门闩的含意。整体含意能够了解为倒数的门栓,仿佛有点“321,芝麻开门”的感觉,CountDownLatch 的作用也正是如此。

CountDownLatch 在创立的时候须要传入一个整数,在这个整数“倒数”到 0 之前,主线程须要始终挂起期待,直到其余的线程都执行之后,主线能力继续执行。

CountDownLatch 执行流程

CountDownLatch 的实现是在其外部创立并保护了一个 volatile 类型的整数计数器,当调用 countDown() 办法时,会尝试将整数计数器 -1,当调用 wait() 办法时,以后线程就会判断整数计数器是否为 0,如果为 0,则持续往下执行,如果不为 0,则使以后线程进入期待状态,直到某个线程将计数器设置为 0,才会唤醒在 await() 办法中期待的线程继续执行。

CountDownLatch 罕用办法

// 线程被挂起直到 count 值为 0 才继续执行
public void await() throws InterruptedException {};   

// 和 await() 相似,只不过期待肯定的工夫后 count 值还没变为 0 的话就会继续执行
public boolean await(long timeout, TimeUnit unit) throws InterruptedException { };  

// 将 count 值减 1
public void countDown() {}; 

总结

应用 CountDownLatch 能够实现期待所有工作执行实现之后再执行主工作的性能,它就如同较量中要期待所有运动员都实现较量之后再颁布排名一样,当然咱们在玩农药的时候也是一样,要等所有人汇合结束之后再开团,这是制胜的要害。而 CountDownLatch 是通过计数器来实现期待性能的,当创立 CountDownLatch 时会设置一个大于 0 的计数器,每次调用 countDown() 办法时计数器的值会 -1,直到计数器值变为 0 时,期待的工作就能够继续执行了。

参考 & 鸣谢

www.jianshu.com/p/128476015902

关注公号「Java 中文社群」查看更多精彩且乏味的文章!

正文完
 0