標籤: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