`

什么时候使用CountDownLatch

    博客分类:
  • Java
 
阅读更多

 

本文由 ImportNew - 张涛 翻译自 howtodoinjava。欢迎加入翻译小组。转载请见文末要求。

 

正如每个Java文档所描述的那样,CountDownLatch是一个同步工具类,它允许一个或多个线程一直等待,直到其他线程的操作执行完后再执行。在Java并发中,countdownlatch的概念是一个常见的面试题,所以一定要确保你很好的理解了它。在这篇文章中,我将会涉及到在Java并发编 程中跟CountDownLatch相关的以下几点:

 

目录

  • CountDownLatch是什么?
  • CountDownLatch如何工作?
  • 在实时系统中的应用场景
  • 应用范例
  • 常见的面试题

CountDownLatch是什么

CountDownLatch是在java1.5被引入的,跟它一起被引入的并发工具类还有CyclicBarrier、Semaphore、ConcurrentHashMapBlockingQueue,它们都存在于java.util.concurrent包下。CountDownLatch这个类能够使一个线程等待其他线程完成各自的工作后再执行。例如,应用程序的主线程希望在负责启动框架服务的线程已经启动所有的框架服务之后再执行。

CountDownLatch是通过一个计数器来实现的,计数器的初始值为线程的数量。每当一个线程完成了自己的任务后,计数器的值就会减1。当计数器值到达0时,它表示所有的线程已经完成了任务,然后在闭锁上等待的线程就可以恢复执行任务。

CountDownLatch的伪代码如下所示:

1
2
3
4
5
6
//Main thread start
//Create CountDownLatch for N threads
//Create and start N threads
//Main thread wait on latch
//N threads completes there tasks are returns
//Main thread resume execution

CountDownLatch如何工作

CountDownLatch.java类中定义的构造函数:

1
2
//Constructs a CountDownLatch initialized with the given count.
public void CountDownLatch(int count) {...}

构造器中的计数值(count)实际上就是闭锁需要等待的线程数量。这个值只能被设置一次,而且CountDownLatch没有提供任何机制去重新设置这个计数值

与CountDownLatch的第一次交互是主线程等待其他线程。主线程必须在启动其他线程后立即调用CountDownLatch.await()方法。这样主线程的操作就会在这个方法上阻塞,直到其他线程完成各自的任务。

其他N 个线程必须引用闭锁对象,因为他们需要通知CountDownLatch对象,他们已经完成了各自的任务。这种通知机制是通过 CountDownLatch.countDown()方法来完成的;每调用一次这个方法,在构造函数中初始化的count值就减1。所以当N个线程都调 用了这个方法,count的值等于0,然后主线程就能通过await()方法,恢复执行自己的任务。

在实时系统中的使用场景

让我们尝试罗列出在java实时系统中CountDownLatch都有哪些使用场景。我所罗列的都是我所能想到的。如果你有别的可能的使用方法,请在留言里列出来,这样会帮助到大家。

  1. 实现最大的并行性:有时我们想同时启动多个线程,实现最大程度的并行性。例如,我们想测试一个单例类。如果我们创建一个初始计数为1的CountDownLatch,并让所有线程都在这个锁上等待,那么我们可以很轻松地完成测试。我们只需调用 一次countDown()方法就可以让所有的等待线程同时恢复执行。
  2. 开始执行前等待n个线程完成各自任务:例如应用程序启动类要确保在处理用户请求前,所有N个外部系统已经启动和运行了。
  3. 死锁检测:一个非常方便的使用场景是,你可以使用n个线程访问共享资源,在每次测试阶段的线程数目是不同的,并尝试产生死锁。

CountDownLatch使用例子

在这个例子中,我模拟了一个应用程序启动类,它开始时启动了n个线程类,这些线程将检查外部系统并通知闭锁,并且启动类一直在闭锁上等待着。一旦验证和检查了所有外部服务,那么启动类恢复执行。

BaseHealthChecker.java:这个类是一个Runnable,负责所有特定的外部服务健康的检测。它删除了重复的代码和闭锁的中心控制代码。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
public abstract class BaseHealthChecker implements Runnable {
 
    private CountDownLatch _latch;
    private String _serviceName;
    private boolean _serviceUp;
 
    //Get latch object in constructor so that after completing the task, thread can countDown() the latch
    public BaseHealthChecker(String serviceName, CountDownLatch latch)
    {
        super();
        this._latch = latch;
        this._serviceName = serviceName;
        this._serviceUp = false;
    }
 
    @Override
    public void run() {
        try {
            verifyService();
            _serviceUp = true;
        } catch (Throwable t) {
            t.printStackTrace(System.err);
            _serviceUp = false;
        } finally {
            if(_latch != null) {
                _latch.countDown();
            }
        }
    }
 
    public String getServiceName() {
        return _serviceName;
    }
 
    public boolean isServiceUp() {
        return _serviceUp;
    }
    //This methos needs to be implemented by all specific service checker
    public abstract void verifyService();
}

NetworkHealthChecker.java:这个类继承了BaseHealthChecker,实现了verifyService()方法。DatabaseHealthChecker.javaCacheHealthChecker.java除了服务名和休眠时间外,与NetworkHealthChecker.java是一样的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class NetworkHealthChecker extends BaseHealthChecker
{
    public NetworkHealthChecker (CountDownLatch latch)  {
        super("Network Service", latch);
    }
 
    @Override
    public void verifyService()
    {
        System.out.println("Checking " + this.getServiceName());
        try
        {
            Thread.sleep(7000);
        }
        catch (InterruptedException e)
        {
            e.printStackTrace();
        }
        System.out.println(this.getServiceName() + " is UP");
    }
}

ApplicationStartupUtil.java:这个类是一个主启动类,它负责初始化闭锁,然后等待,直到所有服务都被检测完。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
public class ApplicationStartupUtil
{
    //List of service checkers
    private static List<BaseHealthChecker> _services;
 
    //This latch will be used to wait on
    private static CountDownLatch _latch;
 
    private ApplicationStartupUtil()
    {
    }
 
    private final static ApplicationStartupUtil INSTANCE = new ApplicationStartupUtil();
 
    public static ApplicationStartupUtil getInstance()
    {
        return INSTANCE;
    }
 
    public static boolean checkExternalServices() throws Exception
    {
        //Initialize the latch with number of service checkers
        _latch = new CountDownLatch(3);
 
        //All add checker in lists
        _services = new ArrayList<BaseHealthChecker>();
        _services.add(new NetworkHealthChecker(_latch));
        _services.add(new CacheHealthChecker(_latch));
        _services.add(new DatabaseHealthChecker(_latch));
 
        //Start service checkers using executor framework
        Executor executor = Executors.newFixedThreadPool(_services.size());
 
        for(final BaseHealthChecker v : _services)
        {
            executor.execute(v);
        }
 
        //Now wait till all services are checked
        _latch.await();
 
        //Services are file and now proceed startup
        for(final BaseHealthChecker v : _services)
        {
            if( ! v.isServiceUp())
            {
                return false;
            }
        }
        return true;
    }
}

现在你可以写测试代码去检测一下闭锁的功能了。

1
2
3
4
5
6
7
8
9
10
11
12
public class Main {
    public static void main(String[] args)
    {
        boolean result = false;
        try {
            result = ApplicationStartupUtil.checkExternalServices();
        } catch (Exception e) {
            e.printStackTrace();
        }
        System.out.println("External services validation completed !! Result was :: "+ result);
    }
}
1
2
3
4
5
6
7
8
9
Output in console:
 
Checking Network Service
Checking Cache Service
Checking Database Service
Database Service is UP
Cache Service is UP
Network Service is UP
External services validation completed !! Result was :: true

常见面试题

可以为你的下次面试准备以下一些CountDownLatch相关的问题:

  • 解释一下CountDownLatch概念?
  • CountDownLatch 和CyclicBarrier的不同之处?
  • 给出一些CountDownLatch使用的例子?
  •  CountDownLatch 类中主要的方法?

下载上述例子的源代码,请点击如下链接:

原文链接: howtodoinjava 翻译: ImportNew.com 张涛
译文链接: http://www.importnew.com/15731.html
转载请保留原文出处、译者和译文链接。]
分享到:
评论

相关推荐

    JAVA多线程CountDownLatch使用详解

    什么时候用CountDownLatch? 在多线程编程中,我们经常会遇到这样一种情况:某个线程需要等待其他线程执行完毕后再继续执行。例如,在上面的代码中,我们需要让MyThread1线程先运行完毕,然后MyThread线程再继续...

    framework-analysis:对使用Hibernate出现的问题进行分析;Spring、MyBatis、AQS、ThreadPoolExecutor、CountDownLatch核心源码分析

    于是乎到现在的Hibernate、MyBatis、Spring、Spring MVC、AQS、ThreadPoolExecutor、CountDownLatch使用场景和核心源码分析。 感觉自己还是真的菜鸡,有太多框架的底层实现都不怎么了解。 当山头被一座一座攻克时,...

    Java进阶教程,面试大全,包罗万象

    Java进阶教程,面试大全1,可参考以下问题: Semaphore-信号灯机制。 synchronized在静态方法和普通方法的...ThreadLocal原理,用的时候需要注意什么。 CountDownLatch和CyclicBarrier的用法,以及相互之间的差别。

    Java进阶教程,面试大全

    Java进阶教程,面试大全1,可参考以下问题: Semaphore-信号灯机制。 synchronized在静态方法和普通方法的...ThreadLocal原理,用的时候需要注意什么。 CountDownLatch和CyclicBarrier的用法,以及相互之间的差别。

    java并发编程框架.pdf

    countdownlatch提供了两个方法,一个是countDown,一个是await, countdownlatch初始化的时候需要传入一个整数,在这个整数倒数到0之前,调用了await方法的程序都必须要等待,然后通过countDown来倒数。

    面试题全集(周瑜).pdf

    * 分布式锁的使用场景是什么? * 有哪些实现方案? 十三、Java微服务架构 * 什么是微服务架构? * 如何拆分微服务? * 怎样设计出高内聚、低耦合的微服务? * 有没有了解过DDD领域驱动设计? * 什么是中台? * 你...

    java面试题合集1,检测知识点

    * 分布式锁的使用场景是什么? 十、ZooKeeper分布式协调器 * 什么是CAP理论? * 什么是BASE理论? * ZooKeeper中的领导者选举的流程是怎样的? * ZooKeeper集群中节点之间数据是如何同步的? 十一、分布式系统 *...

    java并发之并发工具类

    CountDownLatch 的实现是通过一个计数器来实现的,当我们在 new 一个 CountDownLatch 对象的时候需要带入该计数器值,该值就表示了线程的数量。每当一个线程完成自己的任务后,计数器的值就会减 1。当计数器的值变为...

    Java并发工具辅助类代码实例

    下面是一个使用CountDownLatch的示例代码: ```java public class CountDownDemo { public static void main(String[] args) throws InterruptedException { CountDownLatch countDownLatch = new CountDownLatch...

    Java多线程优化方法及使用方式

    1. 使用ThreadPool(线程池)+CountDownLatch(程序计数器):这种方式可以通过线程池来管理线程,CountDownLatch可以用于等待所有线程完成任务。 2. 使用Fork/Join框架:这种方式可以将任务拆分成小任务,并行执行...

    java面试题,180多页,绝对良心制作,欢迎点评,涵盖各种知识点,排版优美,阅读舒心

    【基础】什么时候使用字节流?什么时候用字符流? 26 【基础】GBK与UTF-8的区别 26 【基础】static、final、const的区别 26 final: 26 static: 27 【基础】如何实现对象克隆? 27 【基础】Java序列化与反序列化 27 ...

    JAVA中锁的解决方案.docx

    CountDownLatch cdl = new CountDownLatch(5000); for (int i = 0; i ; i++) { es.execute(() -&gt; { test.increment(); cdl.countDown(); }); } es.shutdown(); try { // 等待 5000 个任务执行完成后,打印...

    JAVA高质量并发详解,多线程并发深入讲解

    - **第17章:CountDownLatch的使用** 探讨`CountDownLatch`的用法,以及如何使用它来协调多个线程的执行顺序。 - **第18章:CyclicBarrier的使用** 介绍`CyclicBarrier`的功能,包括其提供的方法及如何使用它来...

    java八股文多线程面试题.pdf

    - **悲观锁**:通常通过使用排他锁(如 synchronized)来实现,假设最坏的情况发生,即数据在任何时候都可能被修改,因此在访问共享资源时总是先获取锁,确保独占访问。 - **乐观锁**:基于假设数据不会经常冲突,仅...

    大并发编程交流

    - **使用 FutureTask 实现延迟加载**:通过将任务封装到 `FutureTask` 中,可以在需要的时候异步加载数据,避免了创建不必要的资源。 #### 五、线程交互 - **wait/notify 和 notifyAll**:这些方法通常与 `...

    Java下载器的简单实现及实时进度和速度获取

    线程间同步可以使用`java.util.concurrent`包下的工具,如`CountDownLatch`或`CyclicBarrier`。 五、用户界面显示 实时进度和速度可以通过图形用户界面(GUI)如Swing或JavaFX展示。可以创建进度条组件和文本框来...

    Java程序员面试宝典(第2版)

    - **自定义异常**:学习如何创建自定义异常类,并在适当的时候抛出这些异常。 #### 二、核心API篇 **1. Java集合框架** - **List接口**:ArrayList和LinkedList的区别,它们各自的适用场景。 - **Set接口**:...

    多线程执行完后主程序再执行(包括需要子线程返回结果)

    这样的设计可以有效地利用多核处理器的计算能力,并确保主程序在适当的时候进行后续操作。 在Java中,我们可以使用`java.util.concurrent`包中的工具类来实现这种模式。`ExecutorService`接口和它的实现类如`...

    tuling-juc-final.zip

    在深入研究这些代码示例时,我们不仅能够了解这些关键字的基本用法,还能学习如何在实际项目中应用并发编程的最佳实践,例如避免死锁、活锁和饥饿,以及如何合理地使用并发工具类,如`Semaphore`、`CountDownLatch`...

Global site tag (gtag.js) - Google Analytics