java类contdownlatch源码,CountDownLatch源码解析

CountDownLatch是Java并发包下的一个工具类,latch是门闩的意思,顾名思义,CountDownLatch就是有一个门闩挡住了里面的人(线程)出来,当count减到0的时候,门闩就打开了,人(线程)就可以出来了。下面从源码的角度看看CountDownLatch究竟是如何实现的。

内部类

CountDownLatch类中有一个静态内部类 Sync ,它继承自 AbstractQueuedSynchronizer ,所以可以看出,CountDownLatch的功能还是通过AQS来实现的。

构造函数

public CountDownLatch(int count) {

if (count < 0) throw new IllegalArgumentException("count < 0");

this.sync = new Sync(count);

}

复制代码

可以看出要使用CountDownLatch就需要指定count值,且必须大于0,而这个count值最终是赋值给了AQS的state,可以看下面 new Sync(count)的源码,它实际上是set的state值,而这个state是AQS中的属性

Sync(int count) {

setState(count);

}

复制代码

方法

countDown方法

它是调用内部类Sync的releaseShared方法,这个方法会先去通过cas的方式修改state值,如果state修改之前就是0或者修改之后不等于0,那就什么都不需要操作了;如果修改之后state=0,那么就去执行doReleaseShared方法。

public void countDown() {

sync.releaseShared(1);

}

public final boolean releaseShared(int arg) {

if (tryReleaseShared(arg)) {

doReleaseShared();

return true;

}

return false;

}

protected boolean tryReleaseShared(int releases) {

// state=0或者修改之后state<>0,返回false

for (;;) {

int c = getState();

if (c == 0)

return false;

int nextc = c-1;

//修改之后state值=0,返回true

if (compareAndSetState(c, nextc))

return nextc == 0;

}

}

复制代码

//这个方法countDown会调用,await方法在被唤醒后也会调用doReleaseShared

private void doReleaseShared() {

for (;;) {

Node h = head;

if (h != null && h != tail) {

int ws = h.waitStatus;

// 如果状态是signal(-1),cas的方式把它改为0

if (ws == Node.SIGNAL) {

if (!compareAndSetWaitStatus(h, Node.SIGNAL, 0))

continue; // loop to recheck cases

//cas成功修改的话,则去唤醒h的下一个节点

unparkSuccessor(h);

}

else if (ws == 0 &&

!compareAndSetWaitStatus(h, 0, Node.PROPAGATE))

continue; // loop on failed CAS

}

//如果h和head相同了,则跳出循环

if (h == head) // loop if head changed

break;

}

}

复制代码

await方法

await方法是调用该方法的线程处于等待状态(state>0),下面从源码分心一下await方法是如何实现的。

await方法最终调用到了AQS的acquireSharedInterruptibly方法

public final void acquireSharedInterruptibly(int arg)

throws InterruptedException {

if (Thread.interrupted())

throw new InterruptedException();

//如果state=0,则tryAcquireShared方法返回1,则不用等待(也就验证了countdown减到0,才释放线程)

if (tryAcquireShared(arg) < 0)

doAcquireSharedInterruptibly(arg);

}

复制代码

private void doAcquireSharedInterruptibly(int arg)

throws InterruptedException {

//把新node加到node的尾部

final Node node = addWaiter(Node.SHARED);

boolean failed = true;

try {

for (;;) {

final Node p = node.predecessor();

//如果node的prev是head的话

if (p == head) {

//看state是否=0,如果等于0,则r=1,否则r=-1

int r = tryAcquireShared(arg);

if (r >= 0) {

//state=0的时候,把node置为head,然后去唤醒head的下一个节点

//setHeadAndPropagate方法参照下面的解析

setHeadAndPropagate(node, r);

p.next = null; // help GC

failed = false;

return;

}

}

//如果p的status是signal(-1)的话,则执行parkAndCheckInterrupt方法,将该线程挂起

if (shouldParkAfterFailedAcquire(p, node) &&

parkAndCheckInterrupt())

throw new InterruptedException();

}

} finally {

if (failed)

cancelAcquire(node);

}

}

复制代码

private void setHeadAndPropagate(Node node, int propagate) {

Node h = head;

setHead(node);

if (propagate > 0 || h == null || h.waitStatus < 0 ||

(h = head) == null || h.waitStatus < 0) {

Node s = node.next;

if (s == null || s.isShared())

//调用doReleaseShared,去唤醒新head的下一个节点

doReleaseShared();

}

}

复制代码

例子

下面举个例子说明CountDownLatch的用法,而且如果大家是想通过debug的方式跟踪CountDownLatch是如何实现的,那么在断点处的suspend一定要改为Thread,因为在await的时候,线程挂起,而在countDown的时候,首先把head的next节点(暂时称作A节点)唤醒,而此时A节点在await挂起的线程就被唤醒了,继续往下执行,由于await方法会调用setHeadAndPropagate方法,setHeadAndPropagate方法会调用doReleaseShared(countDown也是调用这个方法唤醒线程),所以除了调用countDown的线程,被唤醒的线程也会去唤醒它的下一个节点,所以doReleaseShared方法是被多线程调用的,因此在debug的时候一定要把suspend改为Thread才能看到效果。

public void test() throws InterruptedException {

CountDownLatch countDownLatch = new CountDownLatch(1);

for (int i = 0; i < 5; i++) {

new Thread(() -> {

try {

System.out.println("子线程" + Thread.currentThread().getName() + "await 前");

countDownLatch.await();

System.out.println("子线程" + Thread.currentThread().getName() + "await 后");

} catch (InterruptedException e) {

e.printStackTrace();

}

}).start();

}

Thread.sleep(30000);

countDownLatch.countDown();

System.out.println("完成");

}

复制代码

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值