前言:
CountDownLatch
是一個同步工具類,它允許一個或多個線程一直等待,直到其他線程執行完后再執行。例如,應用程序的主線程希望在負責啟動框架服務的線程已經啟動所有框架服務之后執行。
1 CountDownLatch主要方法
void await():如果當前count
大于0,當前線程將會wait,直到count等于0或者中斷。 PS:當count
等于0的時候,再去調用await()
,
線程將不會阻塞,而是立即運行。后面可以通過源碼分析得到。
boolean await(long timeout, TimeUnit unit):使當前線程在鎖存器倒計數至零之前一直等待,除非線程被中斷或超出了指定的等待時間。
void countDown(): 遞減鎖存器的計數,如果計數到達零,則釋放所有等待的線程。
long getCount() :獲得計數的數量
2 CountDownLatch使用例子
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
|
public class CountDownLatchTest { private static final int N = 4 ; public static void main(String[] args) { final CountDownLatch latch = new CountDownLatch( 4 ); for ( int i= 0 ;i<N;i++) { new Thread(){ public void run() { try { System.out.println( "子線程" +Thread.currentThread().getName()+ "正在執行" ); Thread.sleep( 3000 ); System.out.println( "子線程" +Thread.currentThread().getName()+ "執行完畢" ); latch.countDown(); System.out.println( "剩余計數" +latch.getCount()); } catch (InterruptedException e) { e.printStackTrace(); } }; }.start(); } try { System.out.println( "等待" +N+ "個子線程執行完畢..." ); latch.await(); System.out.println(N+ "個子線程已經執行完畢" ); System.out.println( "繼續執行主線程" ); } catch (InterruptedException e) { e.printStackTrace(); } } } |
子線程Thread-1正在執行
子線程Thread-3正在執行
子線程Thread-2正在執行
等待4個子線程執行完畢...
子線程Thread-0正在執行
子線程Thread-3執行完畢
子線程Thread-2執行完畢
剩余計數2
子線程Thread-1執行完畢
剩余計數1
子線程Thread-0執行完畢
剩余計數3
剩余計數0
4個子線程已經執行完畢
繼續執行主線程
3 CountDownLatch源碼分析
CountDownLatch
是通過計數器的方式來實現,計數器的初始值為線程的數量。每當一個線程完成了自己的任務之后,就會對計數器減1,當計數器的值為0時,表示所有線程完成了任務,此時等待在閉鎖上的線程才繼續執行,從而達到等待其他線程完成任務之后才繼續執行的目的。
構造函數
1
2
3
4
|
public CountDownLatch( int count) { if (count < 0 ) throw new IllegalArgumentException( "count < 0" ); this .sync = new Sync(count); } |
通過傳入一個數值來創建一個CountDownLatch
,數值表示線程可以從等待狀態恢復,countDown
方法必須被調用的次數
countDown方法
1
2
3
|
public void countDown() { sync.releaseShared( 1 ); } |
線程調用此方法對count
進行減1。當count
本來就為0,此方法不做任何操作,當count
比0大,調用此方法進行減1,當new count
為0,釋放所有等待當線程。
countDown方法的內部實現
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
|
/** * Decrements the count of the latch, releasing all waiting threads if * the count reaches zero. * * <p>If the current count is greater than zero then it is decremented. * If the new count is zero then all waiting threads are re-enabled for * thread scheduling purposes. * * <p>If the current count equals zero then nothing happens. */ 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) { // 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 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 )) continue ; // loop to recheck cases unparkSuccessor(h); } else if (ws == 0 && !compareAndSetWaitStatus(h, 0 , Node.PROPAGATE)) continue ; // loop on failed CAS } if (h == head) // loop if head changed break ; } } |
await方法
(1)不帶參數
1
2
3
|
public void await() throws InterruptedException { sync.acquireSharedInterruptibly( 1 ); } |
調用此方法時,當count
為0,直接返回true
,當count
比0大,線程會一直等待,直到count
的值變為0,或者線程被中斷(interepted,此時會拋出中斷異常)。
(2)帶參數
1
2
3
4
|
public boolean await( long timeout, TimeUnit unit) throws InterruptedException { return sync.tryAcquireSharedNanos( 1 , unit.toNanos(timeout)); } |
調用此方法時,當count
為0,直接返回true
,當count
比0大,線程會等待一段時間,等待時間內如果count
的值變為0,返回true
;當超出等待時間,返回false
;或者等待時間內線程被中斷,此時會拋出中斷異常。
await()方法的內部實現
1
2
3
|
public void await() throws InterruptedException { sync.acquireSharedInterruptibly( 1 ); } |
具體如下:
- 1、檢測中斷標志位
-
2、調用
tryAcquireShared
方法來檢查AQS標志位state
是否等于0,如果state
等于0,則說明不需要等待,立即返回,否則進行3 -
3、調用
doAcquireSharedInterruptibly
方法進入AQS同步隊列進行等待,并不斷的自旋檢測是否需要喚醒
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
|
public final void acquireSharedInterruptibly( int arg) throws InterruptedException { if (Thread.interrupted()) throw new InterruptedException(); if (tryAcquireShared(arg) < 0 ) doAcquireSharedInterruptibly(arg); } /* 函數功能:根據AQS的狀態位state來返回值, 如果為state=0,返回 1 如果state=1,則返回-1 */ protected int tryAcquireShared( int acquires) { return (getState() == 0 ) ? 1 : - 1 ; } /** * Acquires in shared interruptible mode. * @param arg the acquire argument */ 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 ; // help GC failed = false ; return ; } } if (shouldParkAfterFailedAcquire(p, node) && parkAndCheckInterrupt()) throw new InterruptedException(); } } finally { if (failed) cancelAcquire(node); } } |
4 CountDownLatch和CyclicBarrier區別
CountDownLatch
和CyclicBarrier
都能夠實現線程之間的等待,只不過它們側重點不同:
-
CountDownLatch
一般用于某個線程A等待若干個其他線程執行完任務之后,它才執行; -
CyclicBarrier
一般用于一組線程互相等待至某個狀態,然后這一組線程再同時執行;
CountDownLatch
是不能夠重用的,而CyclicBarrier
是可以重用的。
到此這篇關于Java多線程之同步工具類CountDownLatch的文章就介紹到這了,更多相關Java多線程 CountDownLatch內容請搜索服務器之家以前的文章或繼續瀏覽下面的相關文章希望大家以后多多支持服務器之家!
原文鏈接:https://juejin.cn/post/7020650400759939103