java多線程之CyclicBarrier源碼解析

前言

本篇將分析CyclicBarrier的源碼,分析結束後,會用一個示例展示CyclicBarrier,並比較CyclicBarrier和CountDownLatch的區別。

1、CyclicBarrier的簡介

CyclicBarrier允許一組線程在觸發屏障之前相互等待,直到達到某一條件才繼續執行;CyclicBarrier在這些線程釋放後,又可以重新使用,所以也稱循環柵欄。

2、分析源碼

2.1、構造方法

//定義在觸發屏障之前必須調用的線程數
public CyclicBarrier(int parties) {
	this(parties, null);
}
public CyclicBarrier(int parties, Runnable barrierAction) {
	if (parties <= 0) throw new IllegalArgumentException();
	    this.parties = parties;
	    this.count = parties;
	    //定義最後一個線程到達屏障時執行的操作
	    this.barrierCommand = barrierAction;
}

2.2、await()方法

public int await() throws InterruptedException, BrokenBarrierException {
	try {
		//調用dowait方法,不需要定義超時時間
        return dowait(false, 0L);
    } catch (TimeoutException toe) {
        throw new Error(toe); // cannot happen
    }
}

private int dowait(boolean timed, long nanos)
	throws InterruptedException, BrokenBarrierException,
		   TimeoutException {
	final ReentrantLock lock = this.lock;
	//獲取“獨佔鎖”
	lock.lock();
	try {
		//獲取“當前代”
		final Generation g = generation;
		//“當前代”損壞,則拋出異常
		if (g.broken)
			throw new BrokenBarrierException();
		// 如果當前線程被中斷
		if (Thread.interrupted()) {
			//終止CyclicBarrier,喚醒CyclicBarrier中所有等待線程
			breakBarrier();
			throw new InterruptedException();
		}
		//計數
		int index = --count;
		//達到屏障(最後一個線程到達)
		//最後一個到達的線程不執行下面的for循環語句
		if (index == 0) {  // tripped
			boolean ranAction = false;
			try {
				final Runnable command = barrierCommand;
				if (command != null)
					//執行初始化傳入的命令操作
					command.run();
				ranAction = true;
				//調用下一代方法
				nextGeneration();
				return 0;
			} finally {
				if (!ranAction)
					breakBarrier();
			}
		}

		// loop until tripped, broken, interrupted, or timed out
		//非最後一個到達的線程全部執行此語句,阻塞在trip.await()方法上
		for (;;) {
			try {
				//如果不是“超時等待”
				if (!timed)
					// 調用condition的await()方法
					trip.await();
				else if (nanos > 0L)
					nanos = trip.awaitNanos(nanos);
			} catch (InterruptedException ie) {
				// 如果等待過程中,線程被中斷,則執行下面的函數
				if (g == generation && ! g.broken) {
					breakBarrier();
					throw ie;
				} else {
					// We're about to finish waiting even if we had not
					// been interrupted, so this interrupt is deemed to
					// "belong" to subsequent execution.
					Thread.currentThread().interrupt();
				}
			}

			if (g.broken)
				throw new BrokenBarrierException();
			// 如果“generation換代了”,則返回index。
			if (g != generation)
				return index;
			//檢查超時
			if (timed && nanos <= 0L) {
				// 如果超時,則終止CyclicBarrier,並喚醒CyclicBarrier中所有等待線程
				breakBarrier();
				throw new TimeoutException();
			}
		}
	} finally {
		//釋放“獨佔鎖”,並喚醒AQS中等待的下一個線程
		lock.unlock();
	}
}

private void nextGeneration() {
    // 調用condition的signalAll()將其隊列中的等待者全部轉移到AQS的隊列中
    trip.signalAll();
    // 重置count
    count = parties;
    // 進入下一代
    generation = new Generation();
}

3、CyclicBarrier與CountDownLatch的區別

  1. 兩者都能實現阻塞一組線程,然後等待喚醒;
  2. 前者是最後一個線程到達直接喚醒,後者是調用countDown()方法;
  3. 前者是通過ReentrantLock的"獨佔鎖"和Conditon來實現,後者是通過AQS的“共享鎖”實現;
  4. 前者可以重複使用,後者只能使用一次;
  5. 前者只能實現多個線程到達後一起運行(多個條件成立才能一起運行);
  6. 後者不僅可以實現一個線程等待多個線程(多個條件成立才能一起運行),還能實現多個線程等待一個線程(多個條件成立並且等待某個特殊信號才能一起運行)

4、示例

public class CyclicBarrierTest {

    public static final CyclicBarrier WORK_THREAD = new CyclicBarrier(3);

    public static void main(String[] args) throws Exception{
        //主線程邏輯
        Thread.sleep(2000);
        for (int i=0; i<3; i++){
            String condition_name = "條件"+i;
            new Thread(() -> { method(); },condition_name).start();
        }
    }

    public static void method(){
        System.out.println("等待的條件是:" + Thread.currentThread().getName());
        try {
            WORK_THREAD.await();
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (BrokenBarrierException e) {
            e.printStackTrace();
        }
        System.out.println("執行完的條件是:" + Thread.currentThread().getName());
    }
}

輸出結果:

等待的條件是:條件0
等待的條件是:條件1
等待的條件是:條件2
執行完的條件是:條件1
執行完的條件是:條件2
執行完的條件是:條件0

結束語

本篇介紹了CyclicBarrier,分析了源碼,用了一段代碼演示了使用方法,並比較了CyclicBarrier和CountDownLatch的區別。

JUC鎖篇章到此就分析完了,下一篇將開啓分析JUC集合。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章