Java中的CountDownLatch源碼解析
一、簡介
1、CountDownLatch類是一個同步輔助裝置,允許一個或多個線程去等待直到另外的線程完成了一組操作。
2、它通過count進行初始化,await方法會阻塞直到當前的count為0由于調(diào)用了countDown方法,之后所有的線程將被釋放并且立即返回結(jié)果。count不能被重置,如果你想count可以重置,請使用CyclicBarrier。
3、CountDownLatch是一個通用的同步工具,可用于多種用途。CountDownLatch初始化使用count作為一個簡單的可開可關(guān)的大門:所有的線程調(diào)用await方法等待在大門里,當一個線程調(diào)用了countDown方法后大門打開
二、源碼分析
public class CountDownLatch { //內(nèi)部類Sync繼承了AQS private static final class Sync extends AbstractQueuedSynchronizer { private static final long serialVersionUID = 4982264981922014374L; //構(gòu)造方法中的count其實就是傳給了AQS的state屬性 Sync(int count) { setState(count); } //得到的AQS的state屬性值 int getCount() { return getState(); } //重寫的AQS的tryAcquireShared,在共享模式的情況下獲取鎖 protected int tryAcquireShared(int acquires) { //獲取鎖的前提是state為0,表示當前未被其他線程占有 return (getState() == 0) ? 1 : -1; } //重寫的AQS的tryReleaseShared,在共享模式下釋放鎖 protected boolean tryReleaseShared(int releases) { // 減count; 當count為0時喚醒 for (;;) { int c = getState(); if (c == 0) //表示釋放鎖的前提是占有鎖,也就是state的屬性值大于0 return false; int nextc = c-1; //state的值減1 if (compareAndSetState(c, nextc)) //利用CAS來改變state的值 return nextc == 0; //當state的值為0時返回true } } } private final Sync sync; /** * 通過count初始化一個CountDownLatch * *在線程調(diào)用await方法后如果想通過,必須執(zhí)行count次countDown方法 * @如果count為負數(shù),則拋出IllegalArgumentException */ public CountDownLatch(int count) { if (count < 0) throw new IllegalArgumentException("count < 0"); this.sync = new Sync(count); } ================================================================================================ 兩個核心方法,其實底層使用的AQS的方法(共享模式下) /** * 調(diào)用await方法會使當前的線程等待除非count的數(shù)值降為0或者中間拋出異常 * * 如果當前的count為0,則該方法立即返回 * *如果當前的count大于0,則當前線程因線程調(diào)度目的而被禁用,并且休眠,直到發(fā)生以下兩種情況之一: *1、調(diào)用countDown方法將count的值降為0 *2、其他的線程打斷了當前線程,使用Thread.interrupted */ public void await() throws InterruptedException { sync.acquireSharedInterruptibly(1); } /** *減count的值,如果count的值為0了,則釋放所有等待的線程 * *如果當前的count值大于0,那么它是被減了。 *如果當前的count值等于0,那么所有等待的線程被喚醒接受線程的調(diào)度 */ public void countDown() { sync.releaseShared(1); } ================================================================================================ /** *返回count的值,用于調(diào)試或者測試 */ public long getCount() { return sync.getCount(); }
三、小練習-模擬王者榮耀單挑
public static void main(String[] args) throws Exception{ CountDownLatch cd = new CountDownLatch(3); Thread beginGame = new Thread(new Runnable() { @Override public void run() { try{ cd.await(); System.out.println("歡迎來到王者榮耀,敵軍還有30秒到達戰(zhàn)場============"); }catch(Exception e){ e.printStackTrace(); } } },"beginGame"); Thread player1 = new Thread(new Runnable() { @Override public void run() { try{ System.out.println("玩家一以準備======="); cd.countDown(); }catch(Exception e){ e.printStackTrace(); } } },"player1"); Thread player2 = new Thread(new Runnable() { @Override public void run() { try{ System.out.println("玩家二以準備======="); cd.countDown(); }catch(Exception e){ e.printStackTrace(); } } },"player2"); beginGame.start(); player1.start(); player2.start(); }
四、總結(jié)
1、請注意,只有在共享模式下才能使用CountDownLatch,因為只有在共享模式下,AQS的state屬性的值才有可能大于1,才有后續(xù)的等待state的值為0,其他的線程才能被喚醒繼續(xù)執(zhí)行的可能,這里針對的多個線程等待!
2、CountDownLatch里面有兩個核心方法:await和countDown。這里注意,await和平時學習的Condition中的await不一樣,這里的await就是線程獲取鎖且必須在state值為0,也就是該資源未被占有的情況下,獲取鎖成功后state的值從0變?yōu)?,表示該線程持有了該資源的鎖。countDown就是將state的屬性值減1。這兩個方法其實都是調(diào)用的AQS的方法執(zhí)行的。
3、初始化傳入的count表示的就是AQS的state屬性的值,可以理解為持有該資源的線程數(shù),其他的線程想要拿到這個資源,必須等到該資源的state的值變?yōu)?才能被喚醒去獲取鎖。也說明了如果該state的值要從N變?yōu)?,需要執(zhí)行N次countDown方法
4、有點類似于線程通信機制的wait/notify。
5、CountDownLatch典型的用法是將一個程序分為n個互相獨立的可解決任務,并創(chuàng)建值為n的CountDownLatch。當每一個任務完成時,都會在這個鎖存器上調(diào)用countDown,等待問題被解決的任務調(diào)用這個鎖存器的await,將他們自己攔住,直至鎖存器計數(shù)結(jié)束。
6、個人理解:線程A和線程B爭奪一個資源,且線程B需要在線程A之后才能執(zhí)行,線程A首先拿到這個資源,然后線程A需要執(zhí)行一個大任務,這個大任務可以分解為N個小任務,所以,創(chuàng)建一個N的count的CountDownLatch,所以,state的值就是N了,線程A每執(zhí)行完一個小任務后,將調(diào)用CountDownLatch的countDown方法將state的值減1,直到最后,所有的小任務執(zhí)行完畢,代表線程A的大任務也就執(zhí)行完畢了,此時,該資源的state的值為0了,代表可以被其他線程爭奪獲取鎖。線程B就可以調(diào)用countDown的await就能夠獲取該資源的鎖,并將state的值變?yōu)?。
到此這篇關(guān)于Java中的CountDownLatch源碼解析的文章就介紹到這了,更多相關(guān)CountDownLatch源碼內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
SpringBoot之@Controller和@RequestMapping的實現(xiàn)原理解讀
這篇文章主要介紹了SpringBoot之@Controller和@RequestMapping的實現(xiàn)原理,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2025-04-04maven install報錯中程序包xxx不存在的問題解決
本文主要介紹了maven install報錯中程序包xxx不存在的問題解決,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2022-05-05SpringBoot引入Redis報org.springframework.data.redis.core.RedisT
這篇文章主要介紹了SpringBoot引入Redis報org.springframework.data.redis.core.RedisTemplate類找不到錯誤問題,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2024-09-09IDEA在SpringBoot項目使用Maven打包后jar包太小問題及解決
這篇文章主要介紹了IDEA在SpringBoot項目使用Maven打包后jar包太小問題及解決,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2024-04-04