CountDownLatch 概述和原始碼分析
概述
原始碼中對這個類的描述如下:
A synchronization aid that allows one or more threads to wait until a set of operations being performed in other threads completes.
意思大概是:
它是一個執行緒同步的助手,能夠讓一個或者多個執行緒在一組操作完成之前等待。
簡單場景(例子)
現在有10個人開會,在10人人全部到達會議室之前,“開會”這個執行緒就要一直等待。
程式碼:
import java.util.concurrent.CountDownLatch;
public class CountDownLatchTest {
public static void main(String[] args) throws InterruptedException {
final CountDownLatch countDownLatch = new CountDownLatch(10);
for (int i = 0; i < 10; i++) {
final int Number = i + 1;
new Thread(new Runnable() {
public void run() {
try {
Thread.sleep((long) (Math.random() * 3000));
System.out.println("No." + Number + " arrived");
} catch (InterruptedException e) {
} finally {
countDownLatch.countDown();
System.out.println("CountDownLatch.count:" +countDownLatch.getCount());
}
}
}).start();
}
System.out.println("Wait");
countDownLatch.await();
System.out.println("Start");
}
}
執行結果:
Wait
No.7 arrived
CountDownLatch.count:9
No.10 arrived
CountDownLatch.count:8
No.4 arrived
CountDownLatch.count:7
No.5 arrived
CountDownLatch.count:6
No.6 arrived
CountDownLatch.count:5
No.8 arrived
CountDownLatch.count:4
No.1 arrived
CountDownLatch.count:3
No.9 arrived
CountDownLatch.count:2
No.2 arrived
CountDownLatch.count:1
No.3 arrived
CountDownLatch.count:0
Start
原始碼分析
單單CountDownLatch這個類的原始碼其實非常少,原始碼如下:
public class CountDownLatch {
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;
}
}
}
private final Sync sync;
public CountDownLatch(int count) {
if (count < 0) throw new IllegalArgumentException("count < 0");
this.sync = new Sync(count);
}
public void await() throws InterruptedException {
sync.acquireSharedInterruptibly(1);
}
public boolean await(long timeout, TimeUnit unit)
throws InterruptedException {
return sync.tryAcquireSharedNanos(1, unit.toNanos(timeout));
}
public void countDown() {
sync.releaseShared(1);
}
public long getCount() {
return sync.getCount();
}
public String toString() {
return super.toString() + "[Count = " + sync.getCount() + "]";
}
}
首先我們可以看到AbstractQueuedSynchronizer這個比較顯眼的類,它的原始碼有2000多行,比較多,要全部看完比較耗時間。
因此,我們主要還是從我們經常使用的幾個方法看起:
1、建構函式
2、await()方法
3、countDown()方法
建構函式
由原始碼最終可知,建構函式最終呼叫了AbstractQueuedSynchronizer類的setState(count)方法:
protected final void setState(int newState) {
state = newState;
}
/**
* The synchronization state.
*/
private volatile int state;
從AbstractQueuedSynchronizer原始碼中我們可以看到state就是一個同步狀態。
await()
由CountDownLatch原始碼可知,await()呼叫了AbstractQueuedSynchronizer的tryAcquireSharedNanos方法:
public final void acquireSharedInterruptibly(int arg)
throws InterruptedException {
if (Thread.interrupted())
throw new InterruptedException();
if (tryAcquireShared(arg) < 0)
doAcquireSharedInterruptibly(arg);
}
這裡可以看到如果中斷就會拋異常,最終如果執行成功會執行到doAcquireSharedInterruptibly這個方法:
/**
* Acquires in shared interruptible mode.
* @param arg the acquire argument
*/
private void doAcquireSharedInterruptibly(int arg)
throws InterruptedException {
final Node node = addWaiter(Node.SHARED);
try {
for (;;) {
final Node p = node.predecessor();
if (p == head) {
int r = tryAcquireShared(arg);
if (r >= 0) {
setHeadAndPropagate(node, r);
p.next = null; // help GC
return;
}
}
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
throw new InterruptedException();
}
} catch (Throwable t) {
cancelAcquire(node);
throw t;
}
}
Node在這裡我們看做是一個等待佇列的連結串列就可以了,在等待結束後會從等待佇列中依次取物件出來執行。比如上面的例子中,等待事件就是“開會”。
這邊的parkAndCheckInterrupt()方法就是阻塞執行緒的方法,原始碼如下:
private final boolean parkAndCheckInterrupt() {
LockSupport.park(this);
return Thread.interrupted();
}
countDown()
由CountDownLatch原始碼可知,await()呼叫了AbstractQueuedSynchronizer的releaseShared方法:
public final boolean releaseShared(int arg) {
if (tryReleaseShared(arg)) {
doReleaseShared();
return true;
}
return false;
}
這邊主要是tryReleaseShared和doReleaseShared兩個方法:
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;
}
}
tryReleaseShared方法很簡單,使用CAS更改state的值。
private void doReleaseShared() {
for (;;) {
Node h = head;
if (h != null && h != tail) {
int ws = h.waitStatus;
if (ws == Node.SIGNAL) {
if (!h.compareAndSetWaitStatus(Node.SIGNAL, 0))
continue; // loop to recheck cases
unparkSuccessor(h);
}
else if (ws == 0 &&
!h.compareAndSetWaitStatus(0, Node.PROPAGATE))
continue; // loop on failed CAS
}
if (h == head) // loop if head changed
break;
}
}
doReleaseShared的作用主要是來喚醒阻塞執行緒。
doReleaseShared裡是一個死迴圈,只有Node連結串列中只有一個head時才會跳出迴圈。
unparkSuccessor就是喚醒被阻塞的執行緒的方法。
在head存在並且狀態為 waitStatus時,會通過CAS將狀態變為0,並且喚醒阻塞的執行緒。
相關文章
- CountDownLatch原始碼分析CountDownLatch原始碼
- JUC之CountDownLatch原始碼分析CountDownLatch原始碼
- JUC之CountDownLatch的原始碼和使用場景分析CountDownLatch原始碼
- CountDownLatch原始碼解析CountDownLatch原始碼
- java原始碼-CountDownLatchJava原始碼CountDownLatch
- 【JUC】JDK1.8原始碼分析之CountDownLatch(五)JDK原始碼CountDownLatch
- 一文搞懂 CountDownLatch 用法和原始碼!CountDownLatch原始碼
- CountDownLatch原始碼閱讀CountDownLatch原始碼
- Java 併發程式設計(十三) -- CountDownLatch原始碼分析Java程式設計CountDownLatch原始碼
- 原始碼分析:CountDownLatch 之倒數計時門栓原始碼CountDownLatch
- Redisson 分散式鎖原始碼 11:Semaphore 和 CountDownLatchRedis分散式原始碼CountDownLatch
- 07 併發工具類CountDownLatch、CyclicBarrier、Semaphore使用及原始碼分析CountDownLatch原始碼
- SpringMVC原始碼分析1:SpringMVC概述SpringMVC原始碼
- Netty 原始碼分析系列(一)Netty 概述Netty原始碼
- JUC原始碼學習筆記2——AQS共享和Semaphore,CountDownLatch原始碼筆記AQSCountDownLatch
- Java併發包原始碼學習系列:同步元件CountDownLatch原始碼解析Java原始碼元件CountDownLatch
- WMRouter使用和原始碼分析原始碼
- scheduleWithFixedDelay和scheduleAtFixedRate原始碼分析原始碼
- 以太坊原始碼分析(30)eth-bloombits和filter原始碼分析原始碼OOMFilter
- GPUImage原始碼閱讀(概述)GPUUI原始碼
- 深入OKHttp原始碼分析(一)----同步和非同步請求流程和原始碼分析HTTP原始碼非同步
- [原始碼解析] PyTorch 分散式(1)------歷史和概述原始碼PyTorch分散式
- Apache HttpClient使用和原始碼分析ApacheHTTPclient原始碼
- Retrofit原始碼分析三 原始碼分析原始碼
- 集合原始碼分析[2]-AbstractList 原始碼分析原始碼
- 集合原始碼分析[1]-Collection 原始碼分析原始碼
- 集合原始碼分析[3]-ArrayList 原始碼分析原始碼
- Guava 原始碼分析之 EventBus 原始碼分析Guava原始碼
- ThreadLocal和ThreadLocalMap原始碼分析thread原始碼
- InheritedWidget的使用和原始碼分析原始碼
- Mybatisi和Spring整合原始碼分析MyBatisSpring原始碼
- ThreadLocal原始碼和圖文分析thread原始碼
- DHCP協議和dhcpcd原始碼分析協議原始碼
- Android 原始碼分析之 AsyncTask 原始碼分析Android原始碼
- 【JDK原始碼分析系列】ArrayBlockingQueue原始碼分析JDK原始碼BloC
- 以太坊原始碼分析(36)ethdb原始碼分析原始碼
- 以太坊原始碼分析(38)event原始碼分析原始碼
- 以太坊原始碼分析(41)hashimoto原始碼分析原始碼