CountDownLatch源码阅读
CountDownLatch介绍
CountDownLatch是JUC中的一个并发工具类,主要作用是阻塞某线程至其他线程完成后,再调用本线程。类似于thread.join()
CountDownLatch基本使用
CountDownLatch countDownLatch=new CountDownLatch(3);//在构造时传入需要等待的线程数
new Thread(()->{
System.out.println(""+Thread.currentThread().getName()+"-执行中");
countDownLatch.countDown();
System.out.println(""+Thread.currentThread().getName()+"-执行完毕");
},"t1").start();
new Thread(()->{
System.out.println(""+Thread.currentThread().getName()+"-执行中");
countDownLatch.countDown();
System.out.println(""+Thread.currentThread().getName()+"-执行完毕");
},"t2").start();
new Thread(()->{
System.out.println(""+Thread.currentThread().getName()+"-执行中");
countDownLatch.countDown();
System.out.println(""+Thread.currentThread().getName()+"-执行完毕");
},"t3").start();
try {
countDownLatch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("所有线程执行完毕")
###########
执行结果是:
t1-执行中
t1-执行完毕
t2-执行中
t2-执行完毕
t3-执行中
t3-执行完毕
所有线程执行完毕
势必主线程调用会等待其余线程执行完毕。
CountDownLatch源码解析
下来对CountDownLatch中主要两方法进行阅读分析。
CountDownLatch.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)//判断State标志是否为0,为0则
doAcquireSharedInterruptibly(arg);
}
protected int tryAcquireShared(int acquires) {
return (getState() == 0) ? 1 : -1; //State==0则返回1,否则返回-1 执行doAcquireSharedInterruptibly()方法
}
private void doAcquireSharedInterruptibly(int arg)
throws InterruptedException {
final Node node = addWaiter(Node.SHARED); //创建一个为SHARED模式的节点添加到队列
boolean failed = true;
try {
for (;;) {
final Node p = node.predecessor();
if (p == head) {
int r = tryAcquireShared(arg);//尝试获得锁
if (r >= 0) { //r>0则获得锁,但是state!=0,表示被挂起状态,代码不会执行。
setHeadAndPropagate(node, r);
p.next = null; // help GC
failed = false;
return;
}
}
//阻塞线程
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
throw new InterruptedException();
}
} finally {
if (failed)
cancelAcquire(node);
}
}
private Node addWaiter(Node mode) {
Node node = new Node(Thread.currentThread(), mode); //将本线程封装成node,并把mode设置为下一节点
// Try the fast path of enq; backup to full enq on failure
Node pred = tail; //尾节点
if (pred != null) {
node.prev = pred; //将尾节点设置为前置节点。
if (compareAndSetTail(pred, node)) { //对尾节点更新
pred.next = node; //替换成功则node设置为尾节点的下一节点
return node;
}
}
enq(node);
return 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) { //注意h=head,所以有两次h.waitStatus判断
Node s = node.next;
if (s == null || s.isShared())/
doReleaseShared(); //唤醒处于 await状态下的线程
}
}
总结:
有线程调用await方法,由于state还不是0,会加入到AQS中进行阻塞,直到其他线程调用.countDown()将state递减至0,该线程才会进行唤醒进行锁争抢。
CountDownLatch.countDown()
public void countDown() {
sync.releaseShared(1);
}
public final boolean releaseShared(int arg) {
if (tryReleaseShared(arg)) {
doReleaseShared();
return true;
}
return false;
}
//更新State,当State==0时,调用doReleaseShared();
protected boolean tryReleaseShared(int releases) {
for (;;) {
int c = getState(); //获取State次数
if (c == 0) //等于0返回false
return false;
int nextc = c-1; //不等0则-1
if (compareAndSetState(c, nextc)) //进行cas操作更新 c的State
return nextc == 0;
}
}
private void doReleaseShared() {
for (;;) { //自旋
Node h = head;
if (h != null && h != tail) {
int ws = h.waitStatus;
if (ws == Node.SIGNAL) {
if (!compareAndSetWaitStatus(h, Node.SIGNAL, 0)) //更改status为0
continue; // 循环重新检查
unparkSuccessor(h); //唤醒线程
}
else if (ws == 0 &&
!compareAndSetWaitStatus(h, 0, Node.PROPAGATE)) //更改status为PROPAGATE
continue; // 循环CAS
}
if (h == head) // 如果头节点被更改则跳出
break;
}
}
总结:实现逻辑简单来说就是每次调用对 State进行-1 直到为0时候进行唤醒线程,进行await()阻塞之后的代码逻辑。