前言
日常開發中多線程是我們經常用到了一種技術手段,通過合理的使用多線程可以極大的提高程序中的處理能力,但是在使用多線程的過程中,我們一定需要特別關注多個線程在處理過程中對後續任務的影響,在處理那些涉及多線程任務和單線程任務需要順序處理的時候,我們就可以通過CountDownLatch來進行控制,接下來我們就來深入瞭解一下CountDownLatch的原理和用法。
源碼分析
- 靜態類:Sync
初始化CountDownLatch便是創建了一個私有的靜態類Sync,Sync繼承了抽象類AbstractQueuedSynchronizer,初始化的過程主要是對標誌量state進行賦值。在Sync中重寫了tryAcquireShared、tryReleaseShared
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) {
for (;;) {
int c = getState();
if (c == 0)
return false;
int nextc = c-1;
if (compareAndSetState(c, nextc))
return nextc == 0;
}
}
}
- countDown:
該方法是對我們賦值的標誌量進行減1的操作,該線程是一個線程安全的操作,通過自旋來獲取標誌量state,並對state進行線程安全的uncafe.compareAndSwapInt()操作
public void countDown() {
sync.releaseShared(1);
}
- await:
該方法主要是用來判斷上方賦值的標誌量state是否已被減爲0,當state爲0時表示所有線程均執行完成,否則進行自旋狀態來不斷嘗試判斷是否爲0,await方法還有一個重載方法,可以設置等待的最長時間,如果直到最大等待時間state還不爲0,則退出自旋繼續執行下面的代碼。
public void await() throws InterruptedException {
sync.acquireSharedInterruptibly(1);
}
public boolean await(long timeout, TimeUnit unit)
throws InterruptedException {
return sync.tryAcquireSharedNanos(1, unit.toNanos(timeout));
}
- getCount:
獲取標誌量state當前的值
public void countDown() {
sync.releaseShared(1);
}
使用場景
A、B兩個任務,A任務通過多線程來進行處理,只有A任務的全部線程都執行完成了纔可以執行B任務,這個時候就可以通過給A任務中加一個公用的CountDownLatch。
使用步驟
- 1 創建CountDownLatch實例,初始化state的值
- 2 創建線程池,在線程池中新建線程,初始化線程中傳入CountDownLatch實例
- 3 在新建線程中處理業務代碼,處理完成則調用countDown方法來對CountDownLatch實例中的state的標誌量減1
- 4 調用CountDownLatch實例中的await方法,如果沒有設置超時時間,則將阻塞當前線程直到標誌量state = 0爲止;如果設置了await方法的超時時間,在超時時間內標誌量state被減爲0則退出線程阻塞,在超時時間內標誌量state未被減爲0,到了超時時間也會退出線程阻塞
- 5 接下來將執行await方法後的邏輯
主線程
public class TestCountDownLatch {
public static void main(String[] args) {
ExecutorService executorService = Executors.newFixedThreadPool(2);
CountDownLatch countDownLatch = new CountDownLatch(10);
for (int i = 0; i < 10; i++) {
executorService.execute(new HaveCountDownLatchThread(countDownLatch, i, new Random()));
}
try {
countDownLatch.await();
System.out.println("查詢當前狀態量state : " +countDownLatch.getCount() + "; 前面的線程已經全部執行完成,可以接着衝啦。。。");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
任務線程
public class HaveCountDownLatchThread implements Runnable{
CountDownLatch countDownLatch;
int i;
Random random;
HaveCountDownLatchThread(CountDownLatch countDownLatch, int i, Random random) {
this.countDownLatch = countDownLatch;
this.i = i;
this.random = random;
}
@Override
public void run() {
int sleep = random.nextInt(1000);
System.out.println("第【" + i + "】個啓動的線程即將睡眠 " + sleep + "毫秒");
try {
Thread.sleep(sleep);
System.out.println("第【" + i + "】個啓動的線程已睡醒,查詢當前的state狀態量:" + countDownLatch.getCount());
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("第【" + i + "】個啓動的線程已睡醒,即將執行countDown方法");
countDownLatch.countDown();
}
}
總結
CountDownLatch是Java中concurrent包中的一個工具類,CountDownLatch可以看作是一個計數器,這個計數器的操作是原子操作:同一時刻只能有一個線程去操作這個計數器,我們初始化CountDownLatch的時候設置一個計數值,計數器中的計數值被線程減爲0之前,任何調用CountDownLatch對象上的await方法都會被阻塞,任何線程調用CountDownLatch對象中的countDown方法則爲計數器減1。一直減到state==0或者超時後,開始執行計數器的await方法後的代碼,所以如果我們將CountDownLatch的源碼查看一下,你會發現,CountDownLatch並沒有那麼複雜,加油!