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

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

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

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

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

构造器中的计数值(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,负责所有特定的外部服务健康的检测。它删除了重复的代码和闭锁的中心控制代码。

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是一样的。

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:这个类是一个主启动类,它负责初始化闭锁,然后等待,直到所有服务都被检测完。

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;
}
}

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

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);
}
}
常见面试题

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

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

地址:http://www.importnew.com/15731.html ;

CountDownLatch使用详解的更多相关文章

  1. 并发之CountDownLatch用法详解

    概念 CountDownLatch 是一个同步工具类,它允许一个或多个线程一直等待,直到其他线程执行完后再执行.例如,应用程序的主线程希望在负责启动框架服务的线程已经启动所有的框架服务之后执行. Co ...

  2. Java—CountDownLatch使用详解

    CountDownLatch介绍 CountDownLatch概述 CountDownLatch一般用作多线程倒计时计数器,强制它们等待其他一组(CountDownLatch的初始化决定)任务执行完成 ...

  3. CountDownLatch原理详解

    介绍 当你看到这篇文章的时候需要先了解AQS的原理,因为本文不涉及到AQS内部原理的讲解. CountDownLatch是一种同步辅助,让我们多个线程执行任务时,需要等待线程执行完成后,才能执行下面的 ...

  4. AQS详解

    一.概述 谈到并发,不得不谈ReentrantLock:而谈到ReentrantLock,不得不谈AbstractQueuedSynchronized(AQS)! 类如其名,抽象的队列式的同步器,AQ ...

  5. Java并发之AQS详解

    一.概述 谈到并发,不得不谈ReentrantLock:而谈到ReentrantLock,不得不谈AbstractQueuedSynchronizer(AQS)! 类如其名,抽象的队列式的同步器,AQ ...

  6. java并发包java.util.concurrent详解

    线程池ThreadPoolExecutor的使用 并发容器之CopyOnWriteArrayList 并发容器之CopyOnWriteArraySet 数据结构之ConcurrentHashMap,区 ...

  7. (转)Java并发包基石-AQS详解

    背景:之前在研究多线程的时候,模模糊糊知道AQS这个东西,但是对于其内部是如何实现,以及具体应用不是很理解,还自认为多线程已经学习的很到位了,贻笑大方. Java并发包基石-AQS详解Java并发包( ...

  8. 【1】AQS详解

    概述: 它内部实现主要是状态变量state和一个FIFO队列来完成,同步队列的头结点是当前获取到同步状态的结点,获取同步状态state失败的线程,会被构造成一个结点加入到同步队列尾部(采用自旋CAS来 ...

  9. Zookeeper客户端Curator使用详解

    Zookeeper客户端Curator使用详解 前提 最近刚好用到了zookeeper,做了一个基于SpringBoot.Curator.Bootstrap写了一个可视化的Web应用: zookeep ...

随机推荐

  1. iOS基础 - 数据库-SQLite

    一.iOS应用数据存取的常用方式 XML属性列表 —— PList NSKeyedArchiver 归档 Preference(偏好设置) SQLite3 Core Data(以面向对象的方式操作数据 ...

  2. 监控系统Opserver

    监控系统Opserver的配置调试   Stack Exchange开源其监控系统Opserver有一段时间了.之前在项目中用过他们的MiniProfile来分析页面执行效率和帮助新人了解项目,当他们 ...

  3. win7 64位iis发生错误w3wp.exe解决方法

    服务器为64的,遇到iis错误w3wp.exe 于是百度,网上说的修改iis权限和修改注册表都不行,后来在msdn上找到解决方法,如下所示 设置iis程序集如下即可

  4. DOS头分析

    DOS头分析 PE文件结构综览: 首先上图片: 看到上面的图片可以清晰的看到PE结构复杂结构式什么样子的.有DOS首部,PE头部,PE节表,很多的表块,最后就是一些调试信息. DOS头由DOS 'MZ ...

  5. c语言:将二进制数按位输出

    问题: 1.输入int 20,其二进制为10100,按位输出10100; 2.或者将1转化为“+”,0转化为“-”,输出就是” + - + - - “; int biTofh(int bi,int l ...

  6. Discuz开源论坛

      Discuz开源论坛本地部署自动生成数据库   这个版本可能比较有点老,但是万变不离其宗,再新的版本都是在已有的基础上更新的,所以掌握方法是最重要的! 先上几张安装成功后的图 (安装成功的论坛首页 ...

  7. Lua里的"switch-case"语句

    Lua本身并没有提供switch-case语句,难道说我们就只能靠不断的"if ... elseif ... "这样冗长的方式来实现选择的功能么?当然不是这样的.Lua提供了功能强 ...

  8. Java笔记:内部类

    1.把一个类放在另一类内部定义,这个定义在其他类里面的类就叫做内部类,包含内部类的类叫做外部类.内部类成员可以直接访问外部类的私有数据,但是外部类不能访问内部类的实现细节. 2.非静态内部类(没有st ...

  9. Arduino 各种模块篇 蓝牙模块 手机蓝牙控制Arduino LED灯

    解决方案. 条件: 1.手机android 商店下载 blueTerm 2.向arduino中载入如下代码: char val; ; void setup() { Serial.begin(); pi ...

  10. linux访问windows共享文件夹

    如有版权问题,纯属巧合! ======================================================= windows 上开启共享目录 比如共享了X盘的share文件 ...