CountDownLatch详解

Posted

tags:

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

参考技术A

一个同步辅助类,在完成一组正在其他线程中执行的操作之前,它允许一个或多个线程一直等待。
常见用法
多个人等一个信号后继续执行操作。例如5个运动员,等一个发令员的枪响。
一个人等多个人的信号。旅游团等所有人签到完成才开始出发。
我们最常见见到使用的地方是zk获取连接的时候

这里了也可以看到很明确的使用方法,countDown直到0,await的线程才会继续执行。

CountDownLatch内部使用了共享锁。如果这里还不知道共享和独占的区别,可以看前面的aqs速读。
获取锁成功的方法很简单

共享锁有个约定,返回有三种情况。

共享锁在tryAcquireShared返回大于0的值的时候,会唤醒其他停顿状态加锁线程。由于没有对state的增加操作,所以当state变成0的时候,所有尝试加锁的线程都会被唤醒。

释放锁的操作,就是把state的值减一,当只有state变成0的时候,才返回true,tryReleaseShared返回true的时候会触发唤醒其他加锁线程的操作。
通过上面的过程,我们可以看到CountDownLatch中的共享锁的加锁和释放锁的过程,下面看看是如何和CountDownLatch结合的。

CountDownLatch的构造里会初始化共享锁,并且设置state的值。

countDown是释放锁,最终会调用到tryReleaseShared。

await是加锁,最终会调用到 tryAcquireShared。
CountDownLatch就是一个不断释放锁的过程。

根据业务情况,增加一个最大等待时间。使用这种方式,需要对失败的各种情况作出业务上的对应处理,否则就出现各种数据不正确的问题。也可以对countDown的线程做好异常处理,最好使用另外一个线程池来处理这些线程。这种情况就需要对业务不能停顿时间特别长,导致线程池的资源被耗光的情况做处理。如果是想通过new Thread避免,就需要考虑线程突然暴涨的问题。

#yyds干货盘点# CountDownLatch详解

CountDownLatch详解

CountDownLatch介绍

从源码可知,其底层是由AQS提供支持,所以其数据结构可以参考AQS的数据结构,而AQS的数据结构核心就是两个虚拟队列: 同步队列sync queue 和条件队列condition queue,不同的条件会有不同的条件队列。CountDownLatch典型的用法是将一个程序分为n个互相独立的可解决任务,并创建值为n的CountDownLatch。当每一个任务完成时,都会在这个锁存器上调用countDown,等待问题被解决的任务调用这个锁存器的await,将他们自己拦住,直至锁存器计数结束。

CountDownLatch源码分析

类的继承关系

CountDownLatch没有显示继承哪个父类或者实现哪个父接口, 它底层是AQS是通过内部类Sync来实现的。

public class CountDownLatch 

类的内部类

CountDownLatch类存在一个内部类Sync,继承自AbstractQueuedSynchronizer,其源代码如下。

private static final class Sync extends AbstractQueuedSynchronizer 
// 版本号
private static final long serialVersionUID = 4982264981922014374L;

// 构造器
Sync(int count)
setState(count);


// 返回当前计数
int getCount()
return getState();


// 试图在共享模式下获取对象状态
protected int tryAcquireShared(int acquires)
return (getState() == 0) ? 1 : -1;


// 试图设置状态来反映共享模式下的一个释放
protected boolean tryReleaseShared(int releases)
// Decrement count; signal when transition to zero
// 无限循环
for (;;)
// 获取状态
int c = getState();
if (c == 0) // 没有被线程占有
return false;
// 下一个状态
int nextc = c-1;
if (compareAndSetState(c, nextc)) // 比较并且设置成功
return nextc == 0;



说明: 对CountDownLatch方法的调用会转发到对Sync或AQS的方法的调用,所以,AQS对CountDownLatch提供支持。

类的属性

可以看到CountDownLatch类的内部只有一个Sync类型的属性:

public class CountDownLatch 
// 同步队列
private final Sync sync;

类的构造函数

public CountDownLatch(int count) 
if (count < 0) throw new IllegalArgumentException("count < 0");
// 初始化状态数
this.sync = new Sync(count);

说明: 该构造函数可以构造一个用给定计数初始化的CountDownLatch,并且构造函数内完成了sync的初始化,并设置了状态数。

核心函数 - await函数

此函数将会使当前线程在锁存器倒计数至零之前一直等待,除非线程被中断。其源码如下

public void await() throws InterruptedException 
// 转发到sync对象上
sync.acquireSharedInterruptibly(1);

说明: 由源码可知,对CountDownLatch对象的await的调用会转发为对Sync的acquireSharedInterruptibly(从AQS继承的方法)方法的调用。

acquireSharedInterruptibly源码如下:

public final void acquireSharedInterruptibly(int arg)
throws InterruptedException
if (Thread.interrupted())
throw new InterruptedException();
if (tryAcquireShared(arg) < 0)
doAcquireSharedInterruptibly(arg);

说明: 从源码中可知,acquireSharedInterruptibly又调用了CountDownLatch的内部类Sync的tryAcquireShared和AQS的doAcquireSharedInterruptibly函数。

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

CountdownLatch 结合 await(maxTime) 和 countdown()

Java CountDownLatch解析(下)

CountDownLatch源码解析

CountDownLatch 的使用

CountDownLatch用法

CountDownLatch,CyclicBarrier,Semaphore