併發柵欄CyclicBarrier---簡單問
背景:前幾天在網上看到關於Java併發包java.concurrent中一個連環炮的面試題,整理下以備不時之需。
CyclicBarrier簡介:
柵欄類似於閉鎖,它能夠阻塞一組執行緒直到某個事件發生;它與閉鎖(CountDownLatch)的區分關鍵在於,閉鎖是所有執行緒等待一個外部事件的發生;而柵欄則是所有執行緒相互等待,直到所有執行緒都到達某一點時才開啟柵欄,然後執行緒可以繼續執行。
問題:
如果想實現所有的執行緒一起等待某個事件的發生,當某個事件發生時,所有執行緒一起開始往下執行的話,有什麼好的辦法嗎?
回答:
可以使用柵欄,Java併發包中的CyclicBarrier。
又問:
你知道CyclicBarrier的實現原理嗎?
回答:
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訊號量是隻允許一定數量的執行緒同時執行,一般用來限制訪問資源的執行緒數量。
又問:
如果你這個時候依然可以說出來你自己更好的實現方式,那麼面試官肯定還會揪著這個繼續問你。