CyclicBarrier源碼淺析

本文所使用的源碼版本是JDK1.7

CyclicBarrier是什麼?

JDK1.7中的介紹如下:

  • A synchronization aid that allows a set of threads to all wait for
    • each other to reach a common barrier point. CyclicBarriers are
    • useful in programs involving a fixed sized party of threads that
    • must occasionally wait for each other. The barrier is called
    • cyclic because it can be re-used after the waiting threads
    • are released.

CyclicBarrier它允許一組線程等待,等待的時間就是執行了await()以後,相當於在await()處建立了一個Barrier,當Barrier被打破(broken),線程纔會繼續運行await方法後面的程序。而該Barrier在broken後可以重用,所以稱它爲循環的屏障點。CyclicBarrier支持一個可選的Runnable命令,在一組線程中的最後一個線程到達屏障點之後(Barrier被broken),該命令會被最後一個到達Barrier前的一個線程所執行。


CyclicBarrier的例子

這裏我使用了F1賽車的發車的例子。

import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.TimeUnit;

public class F1 {

    public static class Car implements Runnable {
        private int carId;
        private final CyclicBarrier barrier;

        public Car(int carId, CyclicBarrier barrier) {
            this.barrier = barrier;
            this.carId = carId;
        }

        @Override
        public void run() {
            try {
                System.out.println(carId+"號車準備發車");
                TimeUnit.MILLISECONDS.sleep(2000);
                barrier.await();//所有的都在這裡阻塞,直到控制信號完成
            } catch (InterruptedException | BrokenBarrierException e) {
                e.printStackTrace();
            }
            System.out.println(carId+"號車起步了");
        }
    }
    public static class Controll implements Runnable{

        @Override
        public void run() {
            System.out.println("所有車都準備好了,信號燈倒計時");
            for(int i=3;i>=0;i--){
                try {
                    TimeUnit.SECONDS.sleep(1);
                    System.out.println((i==0? "發車":i));
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
            System.out.println("執行完控制檯的controller的run()以後,喚醒之前阻塞的方法!");
        }
    }
    public static void main(String[] args) throws InterruptedException {
        CyclicBarrier barrier = new CyclicBarrier(22, new Controll());
        System.out.println("馬來西亞大獎賽正賽開始了");
        TimeUnit.SECONDS.sleep(3);
        for(int i =1;i<23;i++){
            new Thread(new Car(i, barrier)).start();;
        }
    }
}
/*
馬來西亞大獎賽正賽開始了
1號車準備發車
2號車準備發車
3號車準備發車
4號車準備發車
......
22號車準備發車
所有車都準備好了,信號燈倒計時
3
2
1
發車
執行完控制檯的controller的run()以後,喚醒之前阻塞的方法!
5號車起步了
4號車起步了
2號車起步了
......
22號車起步了
1號車起步了

*/

可以知道,所有的賽車(線程)在執行完await()以後開始在Barrier前面等待,當所有的線程都到達了Barrier前以後,會執行Runnable類的Commond(這裏就是信號燈),當Commond執行完run()以後,會打破Barrier,使得等待着的線程繼續執行剩下的任務。

CyclicBarrier源碼

首先我們先來看一下CyclicBarrier裏面有什麼

public class CyclicBarrier {

    /**內部域broken用來表示當前的屏障是否被打破了*/
    private static class Generation {
        boolean broken = false;
    }
    /** lock用於保護屏障入口的鎖 */
    private final ReentrantLock lock = new ReentrantLock();
    /** 用來喚醒阻塞的線程的條件 */
    private final Condition trip = lock.newCondition();
    /** 線程的數量 */
    private final int parties;
    /* 所有線程到達Barrier後執行的任務 */
    private final Runnable barrierCommand;
    /** 當前的柵欄 */
    private Generation generation = new Generation();
    /**實際中仍在等待的線程數*/
    private int count;
}    

然後我們來看一下核心方法await()是如何實現的?

public int await() throws InterruptedException, BrokenBarrierException {
        try {
            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()) {
                breakBarrier();
                throw new InterruptedException();
            }
           //count減1,表示還未到達屏障點的線程數量
           int index = --count;
           //如果所有線程都到了屏障點,那麼就由最後一個到達的線程執行Commond
           if (index == 0) {  // tripped
               //表示是否執行了Commond
               boolean ranAction = false;
               try {
                   final Runnable command = barrierCommand;
                   if (command != null)
                       command.run();
                   ranAction = true;
                   //屏障被打破了,那就換一塊屏障,用來進行下一次的攔截
                   nextGeneration();
                   return 0;
               } finally {
                   //如果沒有執行Commond,也就是發生了意外
                   if (!ranAction)
                       breakBarrier();
               }
           }

            // 線程一直做for循環,直到發生中斷或者超市或者broken
            for (;;) {
                try {
                    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();
                    }
                }

                if (g.broken)
                    throw new BrokenBarrierException();

                if (g != generation)
                    return index;

                if (timed && nanos <= 0L) {
                    breakBarrier();
                    throw new TimeoutException();
                }
            }
        } finally {
            lock.unlock();
        }
    }

breakBarrier()和nextGeneration()

//表示順利執行了Commond,換一下屏障
private void nextGeneration() {
        // 讓阻塞的線程繼續執行下去
        trip.signalAll();
        // 重置count 
        count = parties;
        //換一塊屏障
        generation = new Generation();
    }

//表示Commond執行出現了意外,修改broken的值,然後繼續執行被屏障攔截的線程
private void breakBarrier() {
        //設置broken 爲true
        generation.broken = true;
         // 重置count 
        count = parties;
        // 讓阻塞的線程繼續執行下去
        trip.signalAll();
    }

reset()方法

//就是爲了重置屏障
 public void reset() {
        final ReentrantLock lock = this.lock;
        lock.lock();
        try {
            breakBarrier();   // break the current generation
            nextGeneration(); // start a new generation
        } finally {
            lock.unlock();
        }
    }
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章