同步工具类—CountDownLatch详解

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了同步工具类—CountDownLatch详解相关的知识,希望对你有一定的参考价值。

参考技术A

CountDownLatch 是JDK并发包中提供的一个同步工具类。官方文档对这个同步工具的介绍是:

上面的英文介绍大致意思是: CountDownLatch 的主要功能是让一个或者多个线程等待直到一组在其他线程中执行的操作完成。

观看上面的解释可能并不能直观地说明 CountDownLatch 的作用,下面我们通过一个简单的列子看下 CountDownLatch 的使用。

场景:主人(主线程)请客人(子线程)吃晚饭,需要等待所有客人都到了之后才开饭。我们用 CountDownLatch 来模拟下这个场景。

上面的列子中,主人(master线程)请了5个客人吃饭,每个客人到了之后会将 CountDownLatch 的值减一,主人(master)会一直等待所有客人到来,最后输出”开饭“。

CountDownLatch 的使用方式很简单,下面来看下它的实现原理。

首先我们先看下 CountDownLatch 重要的API

下面我们看下具体API的源代码

在构建 CountDownLatch 对象时需要传入一个int型的初始值,这个值就是计数器的初始值。从上面的代码中可以看出,创建 CountDownLatch 是new了一个 Sync 对象。

Sync对象是基于AQS机制实现的,自己实现了 tryAcquireShared 和 tryReleaseShared 方法。

调用 await 方法其实是调用了AQS的 acquireSharedInterruptibly 方法。

在 acquireSharedInterruptibly 中先判断了下当前线程有没有被中断,假如线程已经被中断了,直接抛出中断异常。否则进入 doAcquireSharedInterruptibly 。

doAcquireSharedInterruptibly的处理逻辑是先判断队列中是否只有当前线程,如果只有当前线程的先尝试获取下资源,如果获取资源成功就直接返回了。获取资源不成功就判断下是否要park当前线程,如果需要park当前线程,
那么当前线程就进入waiting状态。否则在for循环中一直执行上面的逻辑。

熟悉AQS机制的会知道上面的代码其实也是调的AQS的 releaseShared 。 releaseShared 的方法会调到 Sync 中的 tryReleaseShared 。

上面的代码逻辑很简单:status的值是0的话就返回true,否则返回false。返回true的话,就会唤醒AQS队列中 所有 阻塞的线程。


同步工具类CountDownLatch模拟任务同步

【同步工具类】CountDownLatch闭锁任务同步

转载:https://www.cnblogs.com/yangchongxing/p/9214284.html

打过dota的同学都知道,多人一起在线打游戏,每个人的电脑性能不同,所以加载游戏需要的时间也是不同的,只有等大家都加载完成了,游戏才能开始玩,我们就模拟这个过程。

游戏

package concurrent;
import java.util.Random;
import java.util.concurrent.CountDownLatch;
public class DotaGame {
    public static void main(String[] args) throws InterruptedException {
        int N = 6;
        int load = 0;
        Random random = new Random();
        // 计数器为1的闭锁,模拟点击开始游戏按钮
        CountDownLatch startLatch = new CountDownLatch(1);
        // 计数器为N的闭锁,模拟打游戏的玩家
        CountDownLatch endLatch = new CountDownLatch(N);
        for(int i = 1; i <= N; i++ ) {
            // 模拟电脑性能不同需要加载时间
            load = random.nextInt(N);
            // 模拟玩家
            new Thread(new Player("play" + i, load, startLatch, endLatch)).start();
        }
        // 模拟开始游戏按钮
        System.out.println("*** 3V3游戏开始 ***");
        // 通知开始
        startLatch.countDown();
        // 等待所有玩家加载完成
        endLatch.await();
        // 游戏进行中
        System.out.println("...Killing Monster...");
        // 游戏结束
        System.out.println("*** 3V3游戏结束 ***");
    }
}

玩家

package concurrent;
import java.util.concurrent.CountDownLatch;
public class Player implements Runnable {
    private String name = "";//玩家姓名
    private int load;//加载时间
    private CountDownLatch startLatch;
    private CountDownLatch endLatch;
    public Player(String name, int load, CountDownLatch startLatch, CountDownLatch endLatch) {
        this.name = name;
        this.load = load;
        this.startLatch = startLatch;
        this.endLatch = endLatch;
    }
    @Override
    public void run() {
        try {
            // 等待通知开始
            startLatch.await();
            // 加载游戏
            Thread.sleep(load * 1000 * 5);
            // 等待其他玩家
            System.out.println(name + ": 加载完成,等待其他玩家。");
            // 通知加载完成
            endLatch.countDown();
        } catch (Exception e1) {
            e1.printStackTrace();
        }
    }
}

很好用的一个同步工具类

 

以上是关于同步工具类—CountDownLatch详解的主要内容,如果未能解决你的问题,请参考以下文章

同步类

Java并发编程--CountDownLatch

浅谈JUC工具CountDownLatch

多线程总结-JUC中常用的工具类

CountDownLatch同步工具类的使用

同步工具类CountDownLatch模拟任务同步