新書Java併發程式設計系統與模型已上線,歡迎拜讀。
前言
barrier(屏障)與互斥量,讀寫鎖,自旋鎖不同,它不是用來保護臨界區的。相反,它跟條件變數一樣,是用來協同多執行緒一起工作的。
條件變數是多執行緒間傳遞狀態的改變來達到協同工作的效果。屏障是多執行緒各自做自己的工作,如果某一執行緒完成了工作,就等待在屏障那裡,直到其他執行緒的工作都完成了,再一起做別的事。舉個通俗的例子:
1.對於條件變數。在接力賽跑裡,1號隊員開始跑的時候,2,3,4號隊員都站著不動,直到1號隊員跑完一圈,把接力棒給2號隊員,2號隊員收到接力棒後就可以跑了,跑完再給3號隊員。這裡這個接力棒就相當於條件變數,條件滿足後就可以由下一個隊員(執行緒)跑。
2.對於屏障:在百米賽跑裡,比賽沒開始之前,每個運動員都在賽場上自由活動,有的熱身,有的喝水,有的跟教練談論。比賽快開始時,準備完畢的運動員就預備在起跑線上,如果有個運動員還沒準備完(除去特殊情況),他們就一直等,直到運動員都在起跑線上,裁判喊口號後再開始跑。這裡的起跑線就是屏障,做完準備工作的運動員都等在起跑線,直到其他運動員也把準備工作做完。
java.util.concurrent.CyclicBarrier類是一個同步機制。它可以通過一些演算法來同步執行緒處理的過程。換言之,就是所有的執行緒必須等待對方,直到所有的執行緒到達屏障,然後繼續執行。下面這個圖可以說明:
兩個執行緒等待迴圈屏障
兩個執行緒通過呼叫CyclicBarrier的 await() 相互等待對方,一旦所有的執行緒都在CyclicBarrier中等待,然後所有的執行緒一起釋放然後繼續執行。
建立迴圈屏障
當你要建立CyclicBarrier的時候必須指定在釋放他們前有多少個執行緒等待,下面是一個例子:
CyclicBarrier barrier = new CyclicBarrier(2);複製程式碼
在CyclicBarrier等待
下面是在CyclicBarrier處等待:
barrier.await();複製程式碼
你也可以指執行緒等待的超時時間,當等待超時的時候,執行緒依然會被釋放。即使並不是所有的執行緒都開始在CyclicBarrier等待。下面這行程式碼指定超時時間:
barrier.await(10, TimeUnit.SECONDS);複製程式碼
所有執行緒在CyclicBarrier等待,是指:
• 最後一個執行緒到達(呼叫await方法)
• 一個執行緒被被另外一個執行緒中斷(另外一個執行緒呼叫了這個現場的interrupt()方法)
• 其中一個等待的執行緒被中斷
• 其中一個等待的執行緒超時
• 一個外部的執行緒呼叫了yclicBarrier.reset()方法。
CyclicBarrier Action
CyclicBarrier 支援一個Runnable屏障動作。當最後一個執行緒到達的時候,這個Runable物件就可以被執行。你需要將這個Runable屏障動作放置在他的構造器中,就像這樣:
Runnable barrierAction = ... ;
CyclicBarrier barrier = new CyclicBarrier(2, barrierAction);複製程式碼
下面這個例子說明了如何使用CyclicBarrier:
Runnable barrier1Action = new Runnable() {
public void run() {
System.out.println("BarrierAction 1 executed ");
}
};
Runnable barrier2Action = new Runnable() {
public void run() {
System.out.println("BarrierAction 2 executed ");
}
};
CyclicBarrier barrier1 = new CyclicBarrier(2, barrier1Action);
CyclicBarrier barrier2 = new CyclicBarrier(2, barrier2Action);
CyclicBarrierRunnable barrierRunnable1 =
new CyclicBarrierRunnable(barrier1, barrier2);
CyclicBarrierRunnable barrierRunnable2 =
new CyclicBarrierRunnable(barrier1, barrier2);
new Thread(barrierRunnable1).start();
new Thread(barrierRunnable2).start();
Here is the CyclicBarrierRunnable class:複製程式碼
下面是CyclicBarrierRunnable類:
public class CyclicBarrierRunnable implements Runnable{
CyclicBarrier barrier1 = null;
CyclicBarrier barrier2 = null;
public CyclicBarrierRunnable(
CyclicBarrier barrier1,
CyclicBarrier barrier2) {
this.barrier1 = barrier1;
this.barrier2 = barrier2;
}
public void run() {
try {
Thread.sleep(1000);
System.out.println(Thread.currentThread().getName() +
" waiting at barrier 1");
this.barrier1.await();
Thread.sleep(1000);
System.out.println(Thread.currentThread().getName() +
" waiting at barrier 2");
this.barrier2.await();
System.out.println(Thread.currentThread().getName() +
" done!");
} catch (InterruptedException e) {
e.printStackTrace();
} catch (BrokenBarrierException e) {
e.printStackTrace();
}
}
}複製程式碼
下面是控制檯的輸出。但是要注意有時候輸出的順序會發生變化,有時候是Thread-0先列印,有時候是Thread-1先列印。
Thread-0 waiting at barrier 1
Thread-1 waiting at barrier 1
BarrierAction 1 executed
Thread-1 waiting at barrier 2
Thread-0 waiting at barrier 2
BarrierAction 2 executed
Thread-0 done!
Thread-1 done!複製程式碼
小廣告
新書《Java併發程式設計系統與模型》目前已經寫完一部分。但是實際上說實話,並不知道讀者們對java併發系統有哪些比較關心的,而且閉門造車實在是太累,寫出來怕沒人看。所以我放在這裡徵求大家的意見。大家可以直接在評論裡提問或者希望作者重點描寫哪一部分內容,我好有針對性的寫。