併發柵欄CyclicBarrier---簡單問

併發柵欄CyclicBarrier---簡單問java

 

背景:前幾天在網上看到關於Java併發包java.concurrent中一個連環炮的面試題,整理下以備不時之需。面試

CyclicBarrier簡介:併發

柵欄相似於閉鎖,它可以阻塞一組線程直到某個事件發生;它與閉鎖(CountDownLatch)的區分關鍵在於,閉鎖是全部線程等待一個外部事件的發生;而柵欄則是全部線程相互等待,直到全部線程都到達某一點時纔打開柵欄,而後線程能夠繼續執行。app

問題:oop

若是想實現全部的線程一塊兒等待某個事件的發生,當某個事件發生時,全部線程一塊兒開始往下執行的話,有什麼好的辦法嗎?ui

回答:this

可使用柵欄,Java併發包中的CyclicBarrier。spa

又問:線程

你知道CyclicBarrier的實現原理嗎?code

回答:

CyclicBarrier.await方法調用CyclicBarrier.dowait方法,每次調用await方法都會使計數器-1,當減小到0時就會喚醒全部的線程。(計數器count就是線程總數,CyclicBarrier cyclicBarrier = new CyclicBarrier(100);)最核心的部分就是 int index = --count; 和 nextGeneration();方法。

1 public int await() throws InterruptedException, BrokenBarrierException { 2 try { 3 return dowait(false, 0L); 4 } catch (TimeoutException toe) { 5 throw new Error(toe); // cannot happen
6 } 7 }
1 public int await(long timeout, TimeUnit unit) 2 throws InterruptedException, 3 BrokenBarrierException, 4 TimeoutException { 5 return dowait(true, unit.toNanos(timeout)); 6 }
 1 private int dowait(boolean timed, long nanos)  2 throws InterruptedException, BrokenBarrierException,  3 TimeoutException {  4 final ReentrantLock lock = this.lock;  5 lock.lock();  6 try {  7 final Generation g = generation;  8 
 9 if (g.broken) 10 throw new BrokenBarrierException(); 11 
12 if (Thread.interrupted()) { 13 breakBarrier(); 14 throw new InterruptedException(); 15 } 16 
17 int index = --count; // 最核心的部分就是此處1
18 if (index == 0) { // tripped
19 boolean ranAction = false; 20 try { 21 final Runnable command = barrierCommand; 22 if (command != null) 23 command.run(); 24 ranAction = true; 25 nextGeneration(); // 最核心的部分就是此處2
26 return 0; 27 } finally { 28 if (!ranAction) 29 breakBarrier(); 30 } 31 } 32 
33 // loop until tripped, broken, interrupted, or timed out
34 for (;;) { 35 try { 36 if (!timed) 37 trip.await(); 38 else if (nanos > 0L) 39 nanos = trip.awaitNanos(nanos); 40 } catch (InterruptedException ie) { 41 if (g == generation && ! g.broken) { 42 breakBarrier(); 43 throw ie; 44 } else { 45 // We're about to finish waiting even if we had not 46 // been interrupted, so this interrupt is deemed to 47 // "belong" to subsequent execution.
48 Thread.currentThread().interrupt(); 49 } 50 } 51 
52 if (g.broken) 53 throw new BrokenBarrierException(); 54 
55 if (g != generation) 56 return index; 57 
58 if (timed && nanos <= 0L) { 59 breakBarrier(); 60 throw new TimeoutException(); 61 } 62 } 63 } finally { 64 lock.unlock(); 65 } 66 }
1 private void nextGeneration() { 2 // signal completion of last generation
3 trip.signalAll(); 4 // set up next generation
5 count = parties; 6 generation = new Generation(); 7 }

又問:

除此以外,您還知道其它的實現方式嗎?

回答:

方案1:讀寫鎖,剛開始主線程獲取寫鎖,而後全部子線程獲取讀鎖,而後等事件發生時主線程釋放寫鎖;

方案2:CountDownLatch閉鎖,CountDownLatch初始值設爲1,全部子線程調用await方法等待,等事件發生時調用countDown方法計數減爲0;

方案3:Semaphore,Semaphore初始值設爲N,剛開始主線程先調用acquire(N)申請N個信號量,其餘線程調用acquire()阻塞等待,等事件發生時同時主線程釋放N個信號量。

CountDownLatch閉鎖實現模擬以下:

 1 import java.util.concurrent.CountDownLatch;  2 
 3 public class CountDownLatchDemo {  4 
 5 /** 
 6 * 模擬老爸去飯店  7 */ 
 8 public static void fatherToRes()  9 { 10 System.out.println("老爸步行去飯店須要3小時。"); 11 } 12 
13 /** 
14 * 模擬老媽去飯店 15 */ 
16 public static void motherToRes() 17 { 18 System.out.println("老媽擠公交去飯店須要2小時。"); 19 } 20 
21 /** 
22 * 模擬我去飯店 23 */ 
24 public static void meToRes() 25 { 26 System.out.println("我乘地鐵去飯店須要1小時。"); 27 } 28 
29 /** 
30 * 模擬一家人到齊了 31 */ 
32 public static void togetherToEat() 33 { 34 System.out.println("一家人到齊了,開始吃飯"); 35 } 36 
37 
38 private static CountDownLatch latch = new CountDownLatch(3); 39 
40 public static void main(String[] args) throws InterruptedException 41 { 42 
43 new Thread() 44 { 45 public void run() 46 { 47 fatherToRes(); 48 latch.countDown(); 49 }; 50 }.start(); 51 new Thread() 52 { 53 public void run() 54 { 55 motherToRes(); 56 latch.countDown(); 57 }; 58 }.start(); 59 new Thread() 60 { 61 public void run() 62 { 63 meToRes(); 64 latch.countDown(); 65 }; 66 }.start(); 67 
68 latch.await(); 69 togetherToEat(); 70 } 71 }

又問:

您以爲這些方式裏哪一個方式更好呢?

回答:

CountDownLatch閉鎖是等待一組線程執行完畢後才能繼續執行;

CyclicBarrier柵欄是能讓一組線程達到一個同步點時被阻塞,直到最後一個線程達到,阻塞纔會消失,其是能夠循環使用的;

Semaphore信號量是隻容許必定數量的線程同時執行,通常用來限制訪問資源的線程數量。

又問:

若是你這個時候依然能夠說出來你本身更好的實現方式,那麼面試官確定還會揪着這個繼續問你。

相關文章
相關標籤/搜索