java同步並發工具類CountDownLatch、CyclicBarrier和Semaphore

來源:互聯網
上載者:User

標籤:nba   多玩家   hashset   啟動   傳遞   不能   release   syntax   釋放   

閉鎖CountDownLatch

  閉鎖是一種同步工具類,可以延遲線程的進度直到其到達終止狀態。閉鎖的作用相當於一扇門:在閉鎖到達結束狀態之前,這扇門一直是關閉的,並且沒有任何線程能通過,當到達結束狀態時,這扇門會開啟並允許所有的線程通過。當閉鎖到達結束狀態後,將不會再改變狀態,因此這扇門將永遠保持開啟狀態。閉鎖可以用來確保某些活動直到其他活動都完成後才繼續執行,例如:

確保某個計算在其需要的所有資源都被初始化之後才繼續執行。二元閉鎖(包括兩個狀態)可以用來表示“資源R已經被初始化”,而所有需要R的操作都必須現在這個閉鎖上等待。 確保某個服務在其依賴的所有其他服務都已經啟動之後才啟動。每個服務都有一個相關的二元閉鎖。當啟動服務S時,將首先在S依賴的其他服務的閉鎖上等待,在所有依賴的服務都啟動後會釋放閉鎖S,這樣其他依賴S的服務才能繼續執行。 等待直到某個操作的所有參與者(例如,在多玩家遊戲中的所有玩家)都就緒再繼續執行。在這種情況下,當所有玩家都準備就緒時,閉鎖將到達結束狀態。

  CountDownLatch是一種靈活的閉鎖實現,可以在上述各種情況中使用,它可以使一個或多個線程等待一組事件發生。閉鎖狀態包括一個計數器,該計數器被初始化為一個正數,表示需要等待的事件數目量。countDown方法遞減計數器,表示有一個事件已經發生了,而await方法等待計數器達到0,這表示所有需要等待的事件都已經發生。如果計數器的值非0,那麼await會一直阻塞直到計數器為0,或者等待中的線程中斷,或者等待逾時。·

如下代碼展示了CountDownLatch的使用。

 1 import java.util.concurrent.CountDownLatch; 2   3 /** 4  * @author bridge 5  */ 6 public class CountDownLatchTest { 7   8   9     public static void main(String[] args) {10         final CountDownLatch latch = new CountDownLatch(2);11         new Thread() {12             public void run() {13                 try {14                     System.out.println("子線程" + Thread.currentThread().getName() + "正在執行");15                     Thread.sleep(3000);16                     System.out.println("子線程" + Thread.currentThread().getName() + "執行完畢");17                     latch.countDown();18                 } catch (InterruptedException e) {19                     e.printStackTrace();20                 }21             }22         }.start();23  24         new Thread() {25             public void run() {26                 try {27                     System.out.println("子線程" + Thread.currentThread().getName() + "正在執行");28                     Thread.sleep(3000);29                     System.out.println("子線程" + Thread.currentThread().getName() + "執行完畢");30                     latch.countDown();31                 } catch (InterruptedException e) {32                     e.printStackTrace();33                 }34             }35         }.start();36  37         try {38             System.out.println("等待2個子線程執行完畢...");39             latch.await();40             System.out.println("2個子線程已經執行完畢");41             System.out.println("繼續執行主線程");42         } catch (InterruptedException e) {43             e.printStackTrace();44         }45     }46  47  48 }

執行結果如下:

1 子線程Thread-0正在執行2 等待2個子線程執行完畢...3 子線程Thread-1正在執行4 子線程Thread-0執行完畢5 子線程Thread-1執行完畢6 2個子線程已經執行完畢7 繼續執行主線程

 

 

Semaphore

 

計數訊號量(Counting Semaphore)用來控制同時訪問某個特定資源的運算元量,或者同時執行某個指定操作的數量。計數訊號量還可以用來實現某種資源集區,或者對容器施加邊界。

Semaphore中管理著一組虛擬許可,許可的初始數量可通過建構函式來指定,在執行操作時可以首先獲得許可,並在使用以後釋放許可。如果沒有一個許可給訊號量,那麼acquire將阻塞直到有許可(或者直到被中斷或者操作逾時)。release方法將返回一個許可給訊號量。計算訊號量的一種簡化形式是二值訊號量,即初始化為1的Semaphore。二值訊號量可以用作互斥體(mutex),並具備不可重新進入的加鎖語義:誰擁有這個唯一的許可,誰就擁有了互斥鎖。

應用情境:

1.Semaphore可以用於實現資源集區,例如資料庫連接池。可以構造一個固定長度的資源集區。將Semaphore的計數值初始化為池的大小,並從池中擷取一個資源之前首先調用acquire方法擷取一個許可,在將資源返回給池之後調用release釋放許可,那麼acquire將一直阻塞直到資源集區不為空白。

2.使用Semaphore將任何一種容器變成有界阻塞容器。訊號量的計數值會初始化為容器的最大值。add操作在向底層容器添加一個元素之前,首先要擷取一個許可。如果add操作沒有添加任何元素,那麼會立刻釋放許可。同樣,remove操作釋放一個許可,使更多的元素能夠添加到容器中。

  

 1 import java.util.Collections; 2 import java.util.HashSet; 3 import java.util.Set; 4 import java.util.concurrent.Semaphore; 5   6 /** 7  * 訊號量實現有界阻塞容器 8  * 9  * @author bridge10  */11 class BoundedHashSet<t> {12     private final Set<t> set;13     private final Semaphore sem;14  15     public BoundedHashSet(int bound) {16         this.set = Collections.synchronizedSet(new HashSet<t>());17         sem = new Semaphore(bound);18     }19  20     public boolean add(T o) throws InterruptedException {21         //擷取許可22         sem.acquire();23         boolean wasAdded = false;24  25         try {26             wasAdded = set.add(o);27             return wasAdded;28         } finally {29             //如果添加失敗,釋放許可30             if (!wasAdded)31                 sem.release();32         }33  34     }35  36     public boolean remove(Object o) {37  38         boolean wasRemoved = set.remove(o);39         //如果移除成功則釋放許可40         if (wasRemoved)41             sem.release();42         return wasRemoved;43     }44  45 }46  47 public class BoundedHashSetTest {48  49     public static void main(String[] args) {50         final BoundedHashSet<string> set = new BoundedHashSet<string>(5);51         new Thread() {52             @Override53             public void run() {54                 System.out.println("添加");55                 try {56                     set.add("A");57                     set.add("B");58                     set.add("C");59                     set.add("D");60                     set.add("E");61                     set.add("F");62                     System.out.println("添加完畢");63                 } catch (InterruptedException e) {64                     e.printStackTrace();65                 }66             }67         }.start();68         try {69             Thread.sleep(2000);70         } catch (InterruptedException e) {71             e.printStackTrace();72         }73         new Thread() {74             @Override75             public void run() {76                 System.out.println("移除");77                 set.remove("A");78 //                set.remove("B");79 //                set.remove("F");80                 System.out.println("移除完畢");81             }82         }.start();83     }84 }85 

 

運行結果

添加

移除
移除完畢
添加完畢
 
 
CyclicBarrier

 

柵欄(Barrier)類似於閉鎖,它能阻塞一組線程直到某個事件發生。柵欄與閉鎖的關鍵區別在於,所有線程必須同時到達柵欄位置,才能繼續執行。閉鎖用於等待事件,而柵欄用於等待其他線程。柵欄用於實現一些協議,例如幾個家庭決定在某個地方集合:”所有人6:00在麥當勞碰頭,到了以後要等待其他人,之後再討論下一步要做的事情。”

CyclicBarrier可以使一定數量的參與方反覆地在柵欄位置彙集。當線程到達柵欄位置時將調用await方法,這個方法將阻塞直到所有線程都到達柵欄位置。如果所有線程都到達了柵欄位置,那麼柵欄將開啟,此時所有線程都被釋放,而柵欄將被重設以便下次使用。如果對await的調用逾時,或者await阻塞的線程被中斷,那麼柵欄就被認為是打破了,所有阻塞的await調用都將終止並拋出BrokenBarrierExcepiton。

如果成功地通過柵欄,那麼await將為每個線程返回一個唯一的到達索引號,我們可以利用這些索引號來“選舉”產生一個領導線程,並在下一次迭代中由該領導線程執行一些特殊的工作。

CyclicBarrier還可以使你將一個柵欄操作傳遞給建構函式,這是一個Runnable,當成功通過柵欄時會(在一個子任務線程中)執行它,但在阻塞線程被釋放之前是不能執行的。

下面的例子展示了CyclicBarrier的使用。

 

 1 import java.util.concurrent.BrokenBarrierException; 2 import java.util.concurrent.CyclicBarrier; 3   4 /** 5  * @author bridge 6  */ 7 public class CyclicBarrierTest { 8   9     public static void main(String[] args) {10         int N = 4;11         CyclicBarrier cyclicBarrier = new CyclicBarrier(N);12         for (int i = 0; i < N; i++) {13             new Player(cyclicBarrier).start();14         }15  16     }17  18  19 }20  21 class Player extends Thread {22  23     private CyclicBarrier cyclicBarrier;24  25     public Player(CyclicBarrier cyclicBarrier) {26         this.cyclicBarrier = cyclicBarrier;27     }28  29     @Override30     public void run() {31         try {32             Thread.sleep((long) (5000 * Math.random()));33             System.out.println(Thread.currentThread().getName() + "準備好了!");34             cyclicBarrier.await();35         } catch (InterruptedException e) {36             e.printStackTrace();37         } catch (BrokenBarrierException e) {38             e.printStackTrace();39         }40         System.out.println(Thread.currentThread().getName() + " 起跑!");41     }42 }

  

 

執行結果

 

Thread-1準備好了!
Thread-3準備好了!
Thread-2準備好了!
Thread-0準備好了!
Thread-0 起跑!
Thread-1 起跑!
Thread-2 起跑!
Thread-3 起跑!

 

 

java同步並發工具類CountDownLatch、CyclicBarrier和Semaphore

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.