Java併發程式設計的藝術(八)——閉鎖、同步屏障、訊號量詳解
阿新 • • 發佈:2022-05-03
1. 閉鎖:CountDownLatch
1.1 使用場景
若有多條執行緒,其中一條執行緒需要等到其他所有執行緒準備完所需的資源後才能執行,這樣的情況可以使用閉鎖。
1.2 程式碼實現
// 初始化閉鎖,並設定資源個數 CountDownLatch latch = new CountDownLatch(2); Thread t1 = new Thread( new Runnable(){ public void run(){ // 載入資源1 載入資源的程式碼…… // 本資源載入完後,閉鎖-1 latch.countDown(); } } ).start(); Thread t2 = new Thread( new Runnable(){ public void run(){ // 載入資源2 資源載入程式碼…… // 本資源載入完後,閉鎖-1 latch.countDown(); } } ).start(); Thread t3 = new Thread( new Runnable(){ public void run(){ // 本執行緒必須等待所有資源載入完後才能執行 latch.await(); // 當閉鎖數量為0時,await返回,執行接下來的任務 任務程式碼…… } } ).start();
2. 同步屏障:CyclicBarrier
2.1 使用場景
若有多條執行緒,他們到達屏障時將會被阻塞,只有當所有執行緒都到達屏障時才能開啟屏障,所有執行緒同時執行,若有這樣的需求可以使用同步屏障。此外,當屏障開啟的同時還能指定執行的任務。
2.2 閉鎖 與 同步屏障 的區別
- 閉鎖只會阻塞一條執行緒,目的是為了讓該條任務執行緒滿足條件後執行;
- 而同步屏障會阻塞所有執行緒,目的是為了讓所有執行緒同時執行(實際上並不會同時執行,而是儘量把執行緒啟動的時間間隔降為最少)。
2.3 程式碼實現
// 建立同步屏障物件,並制定需要等待的執行緒個數 和 開啟屏障時需要執行的任務 CyclicBarrier barrier = new CyclicBarrier(3,new Runnable(){ public void run(){ //當所有執行緒準備完畢後觸發此任務 } }); // 啟動三條執行緒 for( int i=0; i<3; i++ ){ new Thread( new Runnable(){ public void run(){ // 等待,(每執行一次barrier.await,同步屏障數量-1,直到為0時,開啟屏障) barrier.await(); // 任務 任務程式碼…… } } ).start(); }
3. 訊號量:Semaphore
3.1 使用場景
若有m個資源,但有n條執行緒(n>m),因此同一時刻只能允許m條執行緒訪問資源,此時可以使用Semaphore控制訪問該資源的執行緒數量。
3.2 程式碼實現
// 建立訊號量物件,並給予3個資源 Semaphore semaphore = new Semaphore(3); // 開啟10條執行緒 for ( int i=0; i<10; i++ ) { new Thread( new Runnbale(){ public void run(){ // 獲取資源,若此時資源被用光,則阻塞,直到有執行緒歸還資源 semaphore.acquire(); // 任務程式碼 …… // 釋放資源 semaphore.release(); } } ).start(); }