1. Java多線程之CyclicBarrier

        共 3820字,需瀏覽 8分鐘

         ·

        2021-11-30 14:22

        這里就JUC包中的CyclicBarrier類做相關(guān)介紹

        abstract.jpeg

        概述

        JUC中的CyclicBarrier類是一個并發(fā)控制工具。其可以使線程在柵欄處進(jìn)行等待。當(dāng)指定數(shù)量的線程全部到達(dá)柵欄處后柵欄才會打開,從而使各線程結(jié)束阻塞繼續(xù)向下執(zhí)行。其主要方法如下所示,可以看到在線程全部到達(dá)柵欄時,還可以通過barrierAction參數(shù)設(shè)置準(zhǔn)備打開柵欄前需執(zhí)行的任務(wù)。其中,該任務(wù)由最后一個到達(dá)柵欄的線程負(fù)責(zé)執(zhí)行。具體地,線程調(diào)用await方法實現(xiàn)告訴CyclicBarrier自己已經(jīng)到達(dá)柵欄處,并阻塞等待柵欄打開

        //?創(chuàng)建一個指定計數(shù)器值的CyclicBarrier實例
        public?CyclicBarrier(int?parties);

        //?創(chuàng)建一個指定計數(shù)器值的CyclicBarrier實例,?并指定柵欄打開前需執(zhí)行的任務(wù)
        public?CyclicBarrier(int?parties,?Runnable?barrierAction);

        //?線程阻塞等待柵欄打開
        public?int?await()?throws?InterruptedException,?BrokenBarrierException;

        //?支持超時的await方法
        public?int?await(long?timeout,?TimeUnit?unit)?throws?InterruptedException,?BrokenBarrierException,?TimeoutException;

        //?喚醒其他正在柵欄處被阻塞的線程(即拋出BrokenBarrierException異常),?同時將CyclicBarrier實例恢復(fù)為初始化狀態(tài),以便下一次使用
        public?void?reset();

        基本實踐

        下面即是一個CyclicBarrier的基本實踐示例


        public?class?CyclicBarrierTest1?{

        ????private?static?DateTimeFormatter?formatter?=?DateTimeFormatter.ofPattern("HH:mm:ss");

        ????@Test
        ????public?void?test1()?throws?InterruptedException?{
        ????????ExecutorService?threadPool?=?Executors.newFixedThreadPool(10);

        ????????Runnable?initTask?=?()?->?{
        ????????????info("---------------------------------");
        ????????};

        ????????CyclicBarrier?cyclicBarrier?=?new?CyclicBarrier(3,?initTask);

        ????????Stream.of("張三","李四","王二")
        ????????????.map(?name?->?new?PlayGame(name,?cyclicBarrier)?)
        ????????????.forEach(playGame?->?{
        ????????????????threadPool.execute(playGame);
        ????????????}?);

        ????????//?主線程等待所有任務(wù)執(zhí)行完畢
        ????????try{?Thread.sleep(?120*1000?);?}?catch?(Exception?e)?{}
        ????????info("Game?Over");
        ????}

        ????/**
        ?????*?打印信息
        ?????*?@param?msg
        ?????*/

        ????private?static?void?info(String?msg)?{
        ????????String?time?=?formatter.format(LocalTime.now());
        ????????String?threadName?=?Thread.currentThread().getName();
        ????????String?log?=?"["+time+"]?"+?msg+"?<"+threadName+">";
        ????????System.out.println(log);
        ????}

        ????/**
        ?????*?模擬業(yè)務(wù)耗時
        ?????*/

        ????private?static?void?doSomeWork()?{
        ????????try{
        ????????????Integer?second?=?RandomUtils.nextInt(3,20);
        ????????????System.out.println("second:?"?+?second);
        ????????????Thread.sleep(?second?*?1000?);
        ????????}catch?(Exception?e)?{
        ????????????System.out.println(?"Happen?Exception:?"?+?e.getMessage());
        ????????}
        ????}

        ????@AllArgsConstructor
        ????private?static?class?PlayGame?implements?Runnable{

        ????????private?String?name;

        ????????private?CyclicBarrier?cyclicBarrier;

        ????????@Override
        ????????public?void?run()?{
        ????????????//?模擬業(yè)務(wù)耗時
        ????????????doSomeWork();
        ????????????info(name?+?"?上線");
        ????????????//?阻塞等待其他玩家上線
        ????????????try{
        ????????????????cyclicBarrier.await();
        ????????????}catch?(Exception?e)?{
        ????????????????System.out.println(?"Happen?Exception:?"?+?e);
        ????????????}

        ????????????info(name?+?"?選擇角色?開始");
        ????????????//?模擬業(yè)務(wù)耗時
        ????????????doSomeWork();
        ????????????info(name?+?"?選擇角色?結(jié)束");
        ????????????//?阻塞等待其他玩家選擇角色
        ????????????try{
        ????????????????cyclicBarrier.await();
        ????????????}catch?(Exception?e)?{
        ????????????????System.out.println(?"Happen?Exception:?"?+?e);
        ????????????}

        ????????????info(name?+?"?開始游戲");

        ????????}
        ????}
        }

        從測試結(jié)果可以看出,當(dāng)用戶 開始選擇角色 或 開始游戲時,各線程是同時開始的。至此也可以看出其與CountDownLatch的顯著區(qū)別,后者是一次性的,而前者CyclicBarrier則可以重復(fù)使用

        figure 1.jpeg

        基本原理

        通過上面的代碼示例,可以看到CyclicBarrier與CountDownLatch相比功能很類似。只不過前者可以重復(fù)使用,而后者則是一次性的。但二者在實現(xiàn)上卻大相徑庭,CountDownLatch是直接基于AQS實現(xiàn)的。而CyclicBarrier則是利用ReentrantLock、Condition進(jìn)行實現(xiàn)的。具體地,當(dāng)線程調(diào)用CyclicBarrier的await方法時,如果未達(dá)到指定數(shù)量時,則是通過Condition條件變量的await方法進(jìn)行阻塞的;如果是最后一個線程則會通過Condition條件變量的signalAll方法來喚醒所有被阻塞的線程

        與此同時,由于CyclicBarrier是可重復(fù)使用的。故每一輪結(jié)束后,其內(nèi)部會通過nextGeneration方法生成所謂的下一代CyclicBarrier。本質(zhì)上相當(dāng)于重新實例化了一次CyclicBarrier

        Note

        1. 在實際使用CyclicBarrier過程中,需要非常小心處理BrokenBarrierException異常。本文示例代碼為了簡便,故省略了異常處理過程。因為發(fā)生該異常說明柵欄被損壞了。推薦的處理措施有:一方面,調(diào)用CyclicBarrier的reset方法,來喚醒其他由于調(diào)用await方法而被阻塞的線程以避免一直被阻塞,同時將CyclicBarrier實例恢復(fù)至初始化狀態(tài);另一方面,推薦使用具有超時機(jī)制的await方法,以避免線程被永久性阻塞

        參考文獻(xiàn)

        1. Java并發(fā)編程之美 翟陸續(xù)、薛賓田著
        瀏覽 46
        點贊
        評論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報
        評論
        圖片
        表情
        推薦
        點贊
        評論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報
          
          

            1. 我挺进她的下面疯狂运动 | www大鸡吧 | 岳岳弄的我好舒服 | 亚洲成人午夜剧场 | 快穿之高h真紧粗大 |