正如每个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 类中主要的方法?

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

参考链接:http://www.importnew.com/15731.html

CountDownLatch的简单讲解的更多相关文章

  1. FTP的搭建与虚拟目录作用<之简单讲解>

    操作系统:win7 VS2010编写WebService与在IIS的发布<之简单讲解>中我已经说了IIS安装与使用,不明白的可以跳过去看. 1.添加FTP站点 2. 3. 4. 5. zq ...

  2. WeX5的简单介绍及UI的简单讲解

    WeX5的简单介绍及UI的简单讲解 (2016-01-13 14:49:05) 标签: it 分类: WeX5的初步自学 一.WeX5的简单讲解 1.WeX5是前端快速开发框架,可开发跨端运行应用.是 ...

  3. 简单讲解iOS应用开发中的MD5加密的相关使用

      简单讲解iOS应用开发中的MD5加密的相关使用   作者:文顶顶 字体:[增加 减小] 类型:转载 时间:2015-12-19 我要评论 这篇文章主要介绍了iOS应用开发中的MD5加密的相关使用, ...

  4. Android事件总线分发库EventBus3.0的简单讲解与实践

    Android事件总线分发库EventBus的简单讲解与实践 导语,EventBus大家应该不陌生,EventBus是一款针对Android优化的发布/订阅事件总线.主要功能是替代Intent,Han ...

  5. DES加解密 cbc模式 的简单讲解 && C++用openssl库来实现的注意事项

    DES cbc是基于数据块加密的.数据块的长度为8字节64bit.以数据块为单位循环加密,再拼接.每个数据块加密的秘钥一样,IV向量不同.第一个数据快所需的IV向量,需要我们提供,从第二个数据块开始, ...

  6. thinkphp内置标签简单讲解

    thinkphp内置标签简单讲解 1.volist循环 name 需要遍历的数据 id 类似于foreach中 value offset 截取数据起始位置 length 截取数据的个数 mod 奇偶数 ...

  7. 关于SI4432的问题简单讲解

    对于SX1278 和SI4432的对比性,下面为大家展示对比参数: 由此可以看出的SI4432虽然跟SX1278有部分地方不同,但是整体来说还是差别不大,各有各的长处和短处,性价比上个人还是觉得SI4 ...

  8. 常用函数式接口与Stream API简单讲解

    常用函数式接口与Stream API简单讲解 Stream简直不要太好使啊!!! 常用函数式接口 Supplier<T>,主要方法:T get(),这是一个生产者,可以提供一个T对象. C ...

  9. SpringBoot切面Aop的demo简单讲解

    前言 本篇文章主要介绍的是SpringBoot切面Aop的demo简单讲解. SpringBoot Aop 说明:如果想直接获取工程那么可以直接跳到底部,通过链接下载工程代码. 切面(Aop) 一.概 ...

随机推荐

  1. Nginx:承受3万并发连接数,胜过Apache 10倍

    编者按:Nginx是目前比较重要的开源性负载均衡技术,新浪.网易.六间房等很多网站都将Nginx部署进自己的网站系统架构,并解决部分问题.本文是作者长期的实战经验,很有参考价值. 本文是我撰写的关于搭 ...

  2. Jenkins部署项目

    第三首先部署好Jenkins 新建一个自由项目 svn地址,credentials是指认证,点击Ad那里添加,并选择username和password方式,并输入用户名和密码 H/5 * * * * ...

  3. 远程连接mysql数据库碰到的问题及解决方案

    直接用SecureCRT登录到我的linux服务器,连接mysql后出现以下错误 然后我用root用户登录到mysql查看了我的用户信息,如下 出现问题1的原因有很多种,我这里是因为用错了yangli ...

  4. nginx与Apache的优缺点

    来源:http://itindex.net/detail/46414-apache-nginx 1.nginx相对于apache的优点: 轻量级,同样起web 服务,比apache 占用更少的内存及资 ...

  5. C# to IL 18 Glossary(术语)

  6. oracle故障解决

    修改了字符集,修改错了,然后不能启动 alter system set nls_language='AMERICA'; shutdown immediate; startup 报错 [oracle@o ...

  7. 两个int(32位)整数m和n的二进制表达中,有多少个位(bit)不同

    思路:利用&用算加右移的方法来提取二进制中的每一位数,然后进行比较,查看是否相同. #include<stdio.h> #include<stdlib.h> int m ...

  8. css属性的继承性

    不可继承属性: 盒子模型属性:width height padding border margin 背景相关属性:background相关(background-image background-si ...

  9. out, ref 和 params 的区别和用法

    1. out 参数. 如果你在一个方法中,返回多个相同类型的值,可以考虑返回一个数组. 但是,如果返回多个不同类型的值,返回数组就不可取.这个时候可以考虑使用out参数. out参数就侧重于在一个方法 ...

  10. 为什么 PCB 生产时推荐出 Gerber 给工厂?

    为什么 PCB 生产时推荐出 Gerber 给工厂? 事情是这样的,有一天电工王工,画了一块 PCB,发给 PCB 板厂. 过了几天 PCB 回来了,一看不对呀,这里的丝印怎么少了,那里怎么多了几条线 ...