1. 程式人生 > >Java執行緒池之柵欄

Java執行緒池之柵欄

 通過閉鎖來啟動一組相關的操作,或者等待一組相關的操作結束。閉鎖是一次性物件,一旦進入終止狀態,就不能重置。

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

CyclicBarrier可以使一定數量的參與方法反覆地在柵欄位置彙集,它在並行迭代演算法中非常有用:這種演算法通常將一個問題拆分成一系列獨立的子問題。當執行緒到達柵欄位置時將呼叫await

方法,這個方法將阻塞直到所有執行緒都到達柵欄位置。如果所有執行緒都到達了柵欄位置,那麼柵欄將開啟,此時所有執行緒將被釋放,而柵欄將被重置以便下次使用。如果對await的呼叫超時,或者await阻塞的執行緒被中斷,那麼柵欄將被認為是打破了,所有阻塞的await呼叫都將終止並丟擲BrokenBarrierException。如果成功通過柵欄那麼將為每個執行緒返回一個唯一的到達索引號,我們可以利用這些索引號來“選舉”產生一個領導執行緒,並在下一波迭代中由該領導執行緒執行一些特殊化的工作。CyclicBarrier還可以使你將一個柵欄操作傳遞給建構函式,這是一個Runnable,當成功通過柵欄時會(在一個子執行緒中)執行它,但在阻塞執行緒被釋放之前是不能執行的。

public static void main(String args[]) throws InterruptedException {
        ExecutorService executors = Executors.newFixedThreadPool(10);
        CyclicBarrier cyclicBarrier = new CyclicBarrier(10, new Runnable() {
            @Override
            public void run() {//釋放柵欄時呼叫,也就是最後一個執行完畢的執行緒呼叫
                System.
out.println(Thread.currentThread().getName() + "釋放所有執行緒"); } }); for (int i = 0; i < 10; i++) { executors.submit(new CycRunnable(cyclicBarrier)); } System.out.println("執行完畢"); } private static class CycRunnable implements Runnable { private CyclicBarrier cyclicBarrier; public CycRunnable(CyclicBarrier cyclicBarrier) { this.cyclicBarrier = cyclicBarrier; } @Override public void run() { System.out.println(Thread.currentThread().getName() + "開始執行:"); try { Thread.sleep(1000); System.out.println(Thread.currentThread().getName() + "執行完畢並且等待"); cyclicBarrier.await(); System.out.println(Thread.currentThread().getName() + "等待結束"); } catch (InterruptedException e) { e.printStackTrace(); } catch (BrokenBarrierException e) { e.printStackTrace(); } } }

**注意:**如果執行緒數大於傳入給CyclicBarrier 的值,則多餘執行緒會一直等待,如果小於則所有執行緒會一直等待,CyclicBarrier 也無法釋放。

 柵欄還有另外一種形式,那就是Exchanger,它是一種兩方柵欄,各方在柵欄位置交換資料。當兩方執行不對稱的操作時,Exchanger會非常有用,例如當一個執行緒向緩衝區寫入資料,而另外一個執行緒從緩衝區中讀取資料。這些執行緒可以使用Exchanger來匯合,並將滿的緩衝區與空的緩衝區交換。當兩個執行緒通過Exchanger交換物件時,這種交換就把這兩個物件安全的交給釋出給另一方。
 資料交換的時機取決於應用程式的響應需求。最簡單的方案是,當緩衝區被填滿時,由填充任務進行交換,當緩衝區為空時,由清空任務進行交換。這樣會把需要交換的次數降至最低,但如果新資料的到達率不可預測,那麼一些資料的處理過程就會延遲。另外一個方法是,不僅當緩衝區被填滿時進行交換,並且當緩衝區被填充到一定程度並保持一定時間後,也進行交換。