CountDownLatch描述
作用:
假如有一个主任务三个子任务,我们想让三个子任务执行完,或者执行到一定阶段再执行主任务,
那么我们就可以使用CountDownLatch来实现这个功能
大致的实现逻辑:
CountDownLatch是依赖AQS来实现的
在我看来,AQS实现lock的的一个获取锁和释放锁,都可以分成两大块,就拿获取锁来说:
1.争抢锁,也就是CAS操作state属性
2.处理线程,例如对没有抢到锁的线程调用unsafe的方法让线程陷入等待状态,并放入到队列
那么CountDownLatch的实现方式也是类似的操作,以开头说的例子为例:
1.新建一个countDownLatch,就是给内部的同步器的state设置为3
2.每当调用一次countDownLatch.countDown方法,相当于是释放锁一次,也就是给state属性 -1 操作
3.当主线程执行到 countDownLatch.await方法,那么就相当于获取锁,假如上面的3个线程,只有2个执行了
countDown方法,那么现在state的值1,AQS获取锁的原则是state为0,才表示这个锁没有被别的线程持有,所以
说此时主线程检查state的值为1,不能获取到锁(打个比方,只是为了理解和lock实现的相似度),那么这个时候
会把线程park,并放入同步器的等待队列,知道state的值为0
代码dome
public static void main(String[] args) {
CountDownLatch countDownLatch = new CountDownLatch(3);
IntStream.range(0, 3).forEach(i -> new Thread(() -> {
try {
Thread.sleep(2000);
System.out.println("hello");
} catch (Exception e) {
e.printStackTrace();
} finally {
countDownLatch.countDown();
}
} ) .start());
System.out.println("启动子线程完毕");
try {
countDownLatch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("主线程启动完毕");
}
启动子线程完毕
hello
hello
hello
主线程启动完毕
源码分析
构造方法
CountDownLatch countDownLatch = new CountDownLatch(3);
public CountDownLatch(int count) {
if (count < 0) throw new IllegalArgumentException("count < 0");
this.sync = new Sync(count);
}
Sync(int count) {
setState(count);
}
countDown
public void countDown() {
sync.releaseShared(1);
}
public final boolean releaseShared(int arg) {
if (tryReleaseShared(arg)) {
doReleaseShared();
return true;
}
return false;
}
await
public void await() throws InterruptedException {
sync.acquireSharedInterruptibly(1);
}
public final void acquireSharedInterruptibly(int arg)
throws InterruptedException {
if (Thread.interrupted())
throw new InterruptedException();
if (tryAcquireShared(arg) < 0)
doAcquireSharedInterruptibly(arg);
}
protected int tryAcquireShared(int acquires) {
return (getState() == 0) ? 1 : -1;
}
private void doAcquireSharedInterruptibly(int arg)
throws InterruptedException {
final Node node = addWaiter(Node.SHARED);
boolean failed = true;
try {
for (;;) {
final Node p = node.predecessor();
if (p == head) {
int r = tryAcquireShared(arg);
if (r >= 0) {
setHeadAndPropagate(node, r);
p.next = null;
failed = false;
return;
}
}
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
throw new InterruptedException();
}
} finally {
if (failed)
cancelAcquire(node);
}
}