闭锁java_java多线程学习十::::CountDownLatch闭锁
請看以下的代碼
package rs.thread.day0504;
import java.util.concurrent.CountDownLatch;
/**
* @auther rs
* @date 2019/5/4 20:32
* @email 529811807@qq.com
* @weixinhao javawjs
*
*/
public class Test10_CountDownLatch {
public static void main(String [] args ){
//執行時間
long startTime = System.currentTimeMillis();
//這個是控制幾個線程在執行后,在執行主線程的任務
int count = 6;
CountDownLatch ld = new CountDownLatch(count);
for(int j =0;j
new Thread(new CountDownLatchThread(ld)){}.start();
}
//等待數得到0這個數據在執行
try {
ld.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
long endTime = System.currentTimeMillis();
System.out.println("使用的時間是>>" + (endTime - startTime));
}
}
class CountDownLatchThread implements Runnable{
private CountDownLatch latch ;
public CountDownLatchThread(){}
public CountDownLatchThread(CountDownLatch latch){
this.latch = latch;
}
@Override
public void run() {
for(int i = 0;i<50000;i++){
if(i % 2 == 0){
System.out.println(i);
}
}
//必須控制它減少到0
latch.countDown();
}
}
看了上面的定義和Demo代碼之后,使用就會簡單一點了,一般流程如首先是創建實例 CountDownLatch countDown = new CountDownLatch(2)
需要同步的線程執行完之后,計數-1; countDown.countDown()
需要等待其他線程執行完畢之后,再運行的線程,調用 countDown.await()實現阻塞同步
注意在創建實例是,必須指定初始的計數值,且應大于0
必須有線程中顯示的調用了countDown()計數-1方法;必須有線程顯示調用了 await()方法(沒有這個就沒有必要使用CountDownLatch了)
由于await()方法會阻塞到計數為0,如果在代碼邏輯中某個線程漏掉了計數-1,導致最終計數一直大于0,直接導致死鎖了
鑒于上面一點,更多的推薦 await(long, TimeUnit)來替代直接使用await()方法,至少不會造成阻塞死只能重啟的情況
應用場景
前面給了一個demo演示如何用,那這個東西在實際的業務場景中是否會用到呢?
因為確實在一個業務場景中使用到了,不然也就不會單獨撈出這一節...
電商的詳情頁,由眾多的數據拼裝組成,如可以分成一下幾個模塊交易的收發貨地址,銷量
商品的基本信息(標題,圖文詳情之類的)
推薦的商品列表
評價的內容
....
上面的幾個模塊信息,都是從不同的服務獲取信息,且彼此沒啥關聯;所以為了提高響應,完全可以做成并發獲取數據,如線程1獲取交易相關數據
線程2獲取商品基本信息
線程3獲取推薦的信息
線程4獲取評價信息
....
但是最終拼裝數據并返回給前端,需要等到上面的所有信息都獲取完畢之后,才能返回,這個場景就非常的適合 CountDownLatch來做了在拼裝完整數據的線程中調用 CountDownLatch#await(long, TimeUnit) 等待所有的模塊信息返回
每個模塊信息的獲取,由一個獨立的線程執行;執行完畢之后調用 CountDownLatch#countDown() 進行計數-1
CountDownLatch實現原理就是AQS 的原理
這么好用的功能是怎么實現的呢,下面就來說一說實現它的核心技術原理 AQS。 AQS 全稱 AbstractQueuedSynchronizer,是 java.util.concurrent 中提供的一種高效且可擴展的同步機制。它可以用來實現可以依賴 int 狀態的同步器,獲取和釋放參數以及一個內部FIFO等待隊列,除了CountDownLatch,ReentrantLock、Semaphore 等功能實現都使用了它。
接下來用 CountDownLatch 來分析一下 AQS 的實現。建議看文章的時候先大致看一下源碼,有助于理解下面所說的內容。
在我們的方法中調用 awit()和countDown()的時候,發生了幾個關鍵的調用關系,我畫了一個方法調用圖。
首先在 CountDownLatch 類內部定義了一個 Sync 內部類,這個內部類就是繼承自 AbstractQueuedSynchronizer 的。并且重寫了方法 tryAcquireShared和tryReleaseShared。例如當調用 awit()方法時,CountDownLatch 會調用內部類Sync 的 acquireSharedInterruptibly() 方法,然后在這個方法中會調用 tryAcquireShared 方法,這個方法就是 CountDownLatch 的內部類 Sync 里重寫的 AbstractQueuedSynchronizer 的方法。調用 countDown() 方法同理。
這種方式是使用 AbstractQueuedSynchronizer 的標準化方式,大致分為兩步:
1、內部持有繼承自 AbstractQueuedSynchronizer 的對象 Sync;
2、并在 Sync 內重寫 AbstractQueuedSynchronizer protected 的部分或全部方法,這些方法包括如下幾個:
總結
1、AQS 分為獨占模式和共享模式,CountDownLatch 使用了它的共享模式。
2、AQS 當第一個等待線程(被包裝為 Node)要入隊的時候,要保證存在一個 head 節點,這個 head 節點不關聯線程,也就是一個虛節點。
3、當隊列中的等待節點(關聯線程的,非 head 節點)搶到鎖,將這個節點設置為 head 節點。
4、第一次自旋搶鎖失敗后,waitStatus 會被設置為 -1(SIGNAL),第二次再失敗,就會被 LockSupport 阻塞掛起。
5、如果一個節點的前置節點為 SIGNAL 狀態,則這個節點可以嘗試搶占鎖。
歡迎轉載,轉載請注明出處!
github: rs1314
歡迎關注共公眾號微信 : java微技術
分享我的學習之路和各種java技術,教程資料
總結
以上是生活随笔為你收集整理的闭锁java_java多线程学习十::::CountDownLatch闭锁的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: thinkphp scws mysql_
- 下一篇: java concurrentmap原理