CyclicBarrier底層原理

案例

CyclicBarrier是一個同步輔助類,允許一組線程互相等待,直到到達某個公共屏障點 (common barrier point)。因爲該 barrier 在釋放等待線程後可以重用,所以稱它爲循環 的 barrier。

public class CyclicBarrierTest {

    public static Map<String,Integer> result = new ConcurrentHashMap<>();

    public static CyclicBarrier barrier = new CyclicBarrier(4,()->{
        System.out.println("彙總任務啓動");
        int rs = 0;
        for(Map.Entry<String,Integer> entry:result.entrySet()){
            System.out.println(entry.getKey()+":"+entry.getValue());
            rs += entry.getValue();
        }
        System.out.println("rs="+rs);
    });



    public static void main(String[] args) {
        for(int i=0;i<4;i++){
            Thread t = new Thread(()->{
                System.out.println(Thread.currentThread().getName()+"啓動");
                try {
                    Random random = new Random();

                    result.put(Thread.currentThread().getName(),random.nextInt(10));
                    barrier.await();
                    System.out.println(Thread.currentThread().getName()+"喚醒後繼續執行");
                    // 再次執行barrier.await() 還會觸發彙總任務
                    // barrier.await();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } catch (BrokenBarrierException e) {
                    e.printStackTrace();
                }
            });
            t.start();
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

打印結果

Thread-0啓動
Thread-1啓動
Thread-2啓動
Thread-3啓動
彙總任務啓動
Thread-3:4
Thread-0:1
Thread-1:8
Thread-2:5
rs=18
Thread-3喚醒後繼續執行
Thread-2喚醒後繼續執行
Thread-1喚醒後繼續執行
Thread-0喚醒後繼續執行

可以發現,喚醒的順序跟await的順序是相反的,這涉及到AQS裏面的機制。

Condition

object.wait()和object.notifyAll()實現了通知,需配合synchronized使用。
condition.await()和condition.signalAll()和上面類似,需配合ReentrantLock使用。

public class ConditionTest {
    public static Lock lock = new ReentrantLock();
    public static Condition cond1 = lock.newCondition();
    public static Condition cond2 = lock.newCondition();

    public static void main(String[] args) {
        for(int i=0;i<3;i++) {
            Thread t1 = new Thread(() -> {
                lock.lock();

                try {
                    // 類似於object.wait()  釋放鎖,等待被喚醒然後繼續執行
                    if(Thread.currentThread().getId()==13){
                        System.out.println(Thread.currentThread().getName() + "獲得鎖,cond1.await()");
                        cond1.await();
                    }else{
                        System.out.println(Thread.currentThread().getName() + "獲得鎖,cond2.await()");
                        cond2.await();
                    }
                    System.out.println(Thread.currentThread().getName() + "被喚醒,且帶着鎖休眠1s");
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    lock.unlock();
                }
            });
            t1.start();
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        // 喚醒所有線程,首先得獲取鎖,signal後得釋放鎖
        lock.lock();
        cond1.signalAll();
        System.out.println("喚醒cond1所有線程,同時釋放鎖");
        lock.unlock();

    }
}

可以喚醒指定的與condition綁定的線程

Thread-0獲得鎖,cond2.await()
Thread-1獲得鎖,cond2.await()
Thread-2獲得鎖,cond1.await()
喚醒cond1所有線程,同時釋放鎖
Thread-2被喚醒,且帶着鎖休眠1s

CyclicBarrier原理

具體邏輯實現是

  1. 定義變量 lock , trip , generation , barrierCommand , parties,count 等等
  2. 通過構造函數獲取必須同時到達barrier的線程個數(parties)parties個線程到達barrier時,會執行的動作(barrierCommand)
  3. 如果未達到線程個數,則執行trip.await() 方法(即condition.await()),釋放鎖,等待被喚醒。count–
  4. 如果達到線程個數,執行barrierCommand,trip.signalAll() 喚醒線程並更新generation,parties,count,意味着新的一輪計數開始。
  • 定義變量
	private static class Generation {
	        boolean broken = false;
    }
    /** The lock for guarding barrier entry */
    private final ReentrantLock lock = new ReentrantLock();
    /** Condition to wait on until tripped */
    private final Condition trip = lock.newCondition();
    /** The number of parties */
    private final int parties;
    /* The command to run when tripped */
    private final Runnable barrierCommand;
    /** The current generation */
    private Generation generation = new Generation();
    
	/**
     * Number of parties still waiting. Counts down from parties to 0
     * on each generation.  It is reset to parties on each new
     * generation or when broken.
     */
    private int count;
  • 構造函數
public CyclicBarrier(int parties, Runnable barrierAction) {
    if (parties <= 0) throw new IllegalArgumentException();
    // parties表示“必須同時到達barrier的線程個數”。
    this.parties = parties;
    // count表示“處在等待狀態的線程個數”。
    this.count = parties;
    // barrierCommand表示“parties個線程到達barrier時,會執行的動作”。
    this.barrierCommand = barrierAction;
}
  • await()
private int dowait(boolean timed, long nanos)
    throws InterruptedException, BrokenBarrierException,
           TimeoutException {
    final ReentrantLock lock = this.lock;
    // 獲取“獨佔鎖(lock)”
    lock.lock();
    try {
        // 保存“當前的generation”
        final Generation g = generation;

        // 若“當前generation已損壞”,則拋出異常。
        if (g.broken)
            throw new BrokenBarrierException();

        // 如果當前線程被中斷,則通過breakBarrier()終止CyclicBarrier,喚醒CyclicBarrier中所有等待線程。
        if (Thread.interrupted()) {
            breakBarrier();
            throw new InterruptedException();
        }

       // 將“count計數器”-1
       int index = --count;
       // 如果index=0,則意味着“有parties個線程到達barrier”。
       if (index == 0) {  // tripped
           boolean ranAction = false;
           try {
               // 如果barrierCommand不爲null,則執行該動作。
               final Runnable command = barrierCommand;
               if (command != null)
                   command.run();
               ranAction = true;
               // 喚醒所有等待線程,並更新generation。
               nextGeneration();
               return 0;
           } finally {
               if (!ranAction)
                   breakBarrier();
           }
       }

        // 當前線程一直阻塞,直到“有parties個線程到達barrier” 或 “當前線程被中斷” 或 “超時”這3者之一發生,
        // 當前線程才繼續執行。
        for (;;) {
            try {
                // 如果不是“超時等待”,則調用awati()進行等待;否則,調用awaitNanos()進行等待。
                if (!timed)
                    trip.await();
                else if (nanos > 0L)
                    nanos = trip.awaitNanos(nanos);
            } catch (InterruptedException ie) {
                // 如果等待過程中,線程被中斷,則執行下面的函數。
                if (g == generation && ! g.broken) {
                    breakBarrier();
                    throw ie;
                } else {
                    Thread.currentThread().interrupt();
                }
            }

            // 如果“當前generation已經損壞”,則拋出異常。
            if (g.broken)
                throw new BrokenBarrierException();

            // 如果“generation已經換代”,則返回index。
            if (g != generation)
                return index;

            // 如果是“超時等待”,並且時間已到,則通過breakBarrier()終止CyclicBarrier,喚醒CyclicBarrier中所有等待線程。
            if (timed && nanos <= 0L) {
                breakBarrier();
                throw new TimeoutException();
            }
        }
    } finally {
        // 釋放“獨佔鎖(lock)”
        lock.unlock();
    }
}
  • nextGeneration
	private void nextGeneration() {
        // signal completion of last generation
        trip.signalAll();
        // set up next generation
        count = parties;
        generation = new Generation();
    }
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章