(九)java多線程之CyclicBarrier

本人郵箱: <kco1989@qq.com>
歡迎轉載,轉載請註明網址 http://blog.csdn.net/tianshi_kco
github: https://github.com/kco1989/kco
代碼已經所有託管github有須要的同窗自行下載java

引言

一個同步輔助類,它容許一組線程互相等待,直到到達某個公共屏障點 (common barrier point)。在涉及一組固定大小的線程的程序中,這些線程必須不時地互相等待,此時 CyclicBarrier 頗有用。由於該 barrier 在釋放等待線程後能夠重用,因此稱它爲循環 的 barrier。git

理論

CyclicBarrier 支持一個可選的 Runnable 命令,在一組線程中的最後一個線程到達以後(但在釋放全部線程以前),該命令只在每一個屏障點運行一次。若在繼續全部參與線程以前更新共享狀態,此屏障操做 頗有用。
示例用法:下面是一個在並行分解設計中使用 barrier 的例子:github

class Solver {微信

final int N;
final float[][] data;
final CyclicBarrier barrier;

class Worker implements Runnable {
    int myRow;

    Worker(int row) {
        myRow = row;
    }

    public void run() {
        while (!done()) {
            processRow(myRow);
            try {
                barrier.await();
            } catch (InterruptedException ex) {
                return;
            } catch (BrokenBarrierException ex) {
                return;
            }
        }
    }
}

public Solver(float[][] matrix) {
    data = matrix;
    N = matrix.length;
    barrier = new CyclicBarrier(N,
            new Runnable() {
                public void run() {
                    mergeRows(...);
                }
            });
    for (int i = 0; i < N; ++i)
        new Thread(new Worker(i)).start();

    waitUntilDone();
}

}app

在這個例子中,每一個 worker 線程處理矩陣的一行,在處理完全部的行以前,該線程將一直在屏障處等待。處理完全部的行以後,將執行所提供的 Runnable 屏障操做,併合並這些行。若是合併者肯定已經找到了一個解決方案,那麼 done() 將返回 true,全部的 worker 線程都將終止。dom

若是屏障操做在執行時不依賴於正掛起的線程,則線程組中的任何線程在得到釋放時都能執行該操做。爲方便此操做,每次調用 await() 都將返回能到達屏障處的線程的索引。而後,您能夠選擇哪一個線程應該執行屏障操做,例如:ide

if (barrier.await() == 0) {測試

// log the completion of this iteration

}
對於失敗的同步嘗試,CyclicBarrier 使用了一種要麼所有要麼全不 (all-or-none) 的破壞模式:若是由於中斷、失敗或者超時等緣由,致使線程過早地離開了屏障點,那麼在該屏障點等待的其餘全部線程也將經過 BrokenBarrierException(若是它們幾乎同時被中斷,則用 InterruptedException)以反常的方式離開。this

內存一致性效果:線程中調用 await() 以前的操做 happen-before 那些是屏障操做的一部份的操做,後者依次 happen-before 緊跟在從另外一個線程中對應 await() 成功返回的操做。編碼

  • CyclicBarrier(int parties) 建立一個新的 CyclicBarrier,它將在給定數量的參與者(線程)處於等待狀態時啓動,但它不會在啓動 barrier 時執行預約義的操做。

  • CyclicBarrier(int parties, Runnable barrierAction) 建立一個新的 CyclicBarrier,它將在給定數量的參與者(線程)處於等待狀態時啓動,並在啓動 barrier 時執行給定的屏障操做,該操做由最後一個進入 barrier 的線程執行。

  • await() 在全部參與者都已經在此 barrier 上調用 await 方法以前,將一直等待。

  • await(long timeout, TimeUnit unit) 在全部參與者都已經在此屏障上調用 await 方法以前將一直等待,或者超出了指定的等待時間。

  • getNumberWaiting() 返回當前在屏障處等待的參與者數目。

  • getParties() 返回要求啓動此 barrier 的參與者數目。

  • isBroken() 查詢此屏障是否處於損壞狀態。

  • reset() 將屏障重置爲其初始狀態。

例子

仍是同樣,理論是比較枯燥的,我們仍是舉例來講,比較生動一點.一些教科書舉一些1~100000分批累加例子.我們不舉這種比較無聊的例子.旅遊,相信不少人都喜歡吧!旅遊的套路通常都出發點集合,入住酒店,到旅遊點1,再到旅遊點2,再到旅遊點3,在集合返回.每次都某一個地點時.導遊都會清點人數.不要把人給弄丟.ok,開始編碼...

  • 首先寫一個旅遊類 TourismRunnable,run方法很簡單就調用tourism()旅遊的行程,而後在tourism,再調用旅遊點路程,看代碼..

public class TourismRunnable implements Runnable{
    CyclicBarrier cyclicBarrier;
    Random random;
    public TourismRunnable(CyclicBarrier cyclicBarrier) {
        this.cyclicBarrier = cyclicBarrier;
        this.random = new Random();
    }

    @Override
    public void run() {
        tourism();
    }

    /**
     * 旅遊過程
     */
    private void tourism() {
        goToStartingPoint();
        goToHotel();
        goToTourismPoint1();
        goToTourismPoint2();
        goToTourismPoint3();
        goToEndPoint();
    }

    /**
     * 裝備返程
     */
    private void goToEndPoint() {
        goToPoint("飛機場,準備登機回家");
    }

    /**
     * 到達旅遊點3
     */
    private void goToTourismPoint3() {
        goToPoint("旅遊點3");
    }

    /**
     * 到達旅遊點2
     */
    private void goToTourismPoint2() {
        goToPoint("旅遊點2");
    }

    /**
     * 到達旅遊點1
     */
    private void goToTourismPoint1() {
        goToPoint("旅遊點1");
    }

    /**
     * 入住酒店
     */
    private void goToHotel() {
        goToPoint("酒店");
    }

    /**
     * 出發點集合
     */
    private void goToStartingPoint() {
        goToPoint("出發點");
    }

    private int getRandomTime(){
        int time = this.random.nextInt(400) + 100;
        try {
            Thread.sleep(time);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return time;
    }

    private void goToPoint(String point){
        try {
            String name = Thread.currentThread().getName();
            System.out.println(name + " 花了 " + getRandomTime() + " 時間纔到了" + point);
            cyclicBarrier.await();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

在每一個旅遊點,每一個人旅遊所花的時間是隨機的,有些人玩的比較久一點,有些則蜻蜓點水,拍拍照就完事了

  • 再寫一個測試類,讓一羣小朋友去旅遊吧

public class TestMain {

    public static void main(String[] args) {
        String name = "明剛紅麗黑白";
        CyclicBarrier cyclicBarrier = new CyclicBarrier(name.length(), new Runnable() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName() +
                        " 清點人數,1,2,3...,ok,人到齊了,準備出發..... go go go....");
            }
        });
        List<Thread> tourismThread = new ArrayList<>();
        for (char ch : name.toCharArray()){
            tourismThread.add(new Thread(new TourismRunnable(cyclicBarrier), "小" + ch));
        }
        for (Thread thread : tourismThread){
            thread.start();
        }
    }
}

運行結果:

小剛 花了 131 時間纔到了出發點
小黑 花了 237 時間纔到了出發點
小麗 花了 250 時間纔到了出發點
小紅 花了 335 時間纔到了出發點
小明 花了 379 時間纔到了出發點
小白 花了 398 時間纔到了出發點
小白 清點人數,1,2,3...,ok,人到齊了,準備出發..... go go go....
小紅 花了 128 時間纔到了酒店
小剛 花了 156 時間纔到了酒店
小黑 花了 240 時間纔到了酒店
小白 花了 280 時間纔到了酒店
小明 花了 492 時間纔到了酒店
小麗 花了 499 時間纔到了酒店
小麗 清點人數,1,2,3...,ok,人到齊了,準備出發..... go go go....
小麗 花了 188 時間纔到了旅遊點1
小剛 花了 315 時間纔到了旅遊點1
小明 花了 374 時間纔到了旅遊點1
小白 花了 395 時間纔到了旅遊點1
小黑 花了 428 時間纔到了旅遊點1
小紅 花了 496 時間纔到了旅遊點1
小紅 清點人數,1,2,3...,ok,人到齊了,準備出發..... go go go....
小明 花了 206 時間纔到了旅遊點2
小剛 花了 223 時間纔到了旅遊點2
小紅 花了 302 時間纔到了旅遊點2
小白 花了 308 時間纔到了旅遊點2
小黑 花了 317 時間纔到了旅遊點2
小麗 花了 400 時間纔到了旅遊點2
小麗 清點人數,1,2,3...,ok,人到齊了,準備出發..... go go go....
小白 花了 100 時間纔到了旅遊點3
小麗 花了 132 時間纔到了旅遊點3
小紅 花了 157 時間纔到了旅遊點3
小黑 花了 165 時間纔到了旅遊點3
小剛 花了 375 時間纔到了旅遊點3
小明 花了 416 時間纔到了旅遊點3
小明 清點人數,1,2,3...,ok,人到齊了,準備出發..... go go go....
小剛 花了 100 時間纔到了飛機場,準備登機回家
小黑 花了 137 時間纔到了飛機場,準備登機回家
小紅 花了 232 時間纔到了飛機場,準備登機回家
小明 花了 260 時間纔到了飛機場,準備登機回家
小麗 花了 264 時間纔到了飛機場,準備登機回家
小白 花了 394 時間纔到了飛機場,準備登機回家
小白 清點人數,1,2,3...,ok,人到齊了,準備出發..... go go go....

ok, 運行結果一目瞭然,中途沒有落下任何一我的!CyclicBarrier這個類,仍是比較容易使用的

打賞

若是以爲個人文章寫的還過得去的話,有錢就捧個錢場,沒錢給我捧我的場(幫我點贊或推薦一下)
微信打賞
支付寶打賞

相關文章
相關標籤/搜索