Java并發(fā)JUC——并發(fā)流程控制

什么是并發(fā)流程控制

  • 控制并發(fā)流程的工具類,作用就是幫助我們程序員更容易的讓線程之間進(jìn)行合作
  • 讓線程之間相互配合,來(lái)滿足業(yè)務(wù)需求
  • 比如,讓線程A等待線程B執(zhí)行完畢后在執(zhí)行等合作策略

常見(jiàn)的控制并發(fā)流程工具類

CountDownLatch

CountDownLatch是一個(gè)同步工具類,用來(lái)協(xié)調(diào)多個(gè)線程之間的同步,或者說(shuō)起到線程之間的通信(而不是用作互斥的作用)。

CountDownLatch能夠使一個(gè)線程在等待另外一些線程完成各自工作之后,再繼續(xù)執(zhí)行。使用一個(gè)計(jì)數(shù)器進(jìn)行實(shí)現(xiàn)。計(jì)數(shù)器初始值為線程的數(shù)量。當(dāng)每一個(gè)線程完成自己任務(wù)后,計(jì)數(shù)器的值就會(huì)減一。當(dāng)計(jì)數(shù)器的值為0時(shí),表示所有的線程都已經(jīng)完成一些任務(wù),然后在CountDownLatch上等待的線程就可以恢復(fù)執(zhí)行接下來(lái)的任務(wù)。

CountDownLatch的主要方法

  • public CountDownLatch(int count):僅有一個(gè)構(gòu)造函數(shù),參數(shù)count為需要倒數(shù)的數(shù)值
  • await():調(diào)用await()方法的線程會(huì)被掛起,它會(huì)等待直到count值為0才繼續(xù)執(zhí)行
  • public void countDown():將count值減1,直到為0時(shí),等待的線程會(huì)被喚醒

圖解await和countDown方法

CountDownLatch的用法

  • CountDownLatch典型用法:1、某一線程在開(kāi)始運(yùn)行前等待n個(gè)線程執(zhí)行完畢。將CountDownLatch的計(jì)數(shù)器初始化為new CountDownLatch(n),每當(dāng)一個(gè)任務(wù)線程執(zhí)行完畢,就將計(jì)數(shù)器減1 countdownLatch.countDown(),當(dāng)計(jì)數(shù)器的值變?yōu)?時(shí),在CountDownLatch上await()的線程就會(huì)被喚醒。一個(gè)典型應(yīng)用場(chǎng)景就是啟動(dòng)一個(gè)服務(wù)時(shí),主線程需要等待多個(gè)組件加載完畢,之后再繼續(xù)執(zhí)行。
/**
 * @Description: 工廠中,質(zhì)檢,5個(gè)工人檢查,所有人都認(rèn)為通過(guò),才通過(guò)
 */
public class CountDownLatchDemo1 {

    public static void main(String[] args) throws InterruptedException {
        CountDownLatch countDownLatch = new CountDownLatch(5);

        ExecutorService executorService = Executors.newFixedThreadPool(5);
        for (int i = 0; i < 5; i++) {
            final int no = i+1;
            Runnable runnable = new Runnable() {
                @Override
                public void run() {
                    try {
                        Thread.sleep(new Random().nextInt(10000));
                        System.out.println("NO." + no + "完成了檢查");
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    } finally {
                        countDownLatch.countDown();
                    }
                }
            };
            executorService.submit(runnable);
        }

        System.out.println("等待5個(gè)人檢查完......");

        countDownLatch.await();
        System.out.println("所有人都完成了工作,等待進(jìn)入下一環(huán)節(jié)");
    }
}
  • CountDownLatch典型用法:2、實(shí)現(xiàn)多個(gè)線程開(kāi)始執(zhí)行任務(wù)的最大并行性。注意是并行性,不是并發(fā),強(qiáng)調(diào)的是多個(gè)線程在某一時(shí)刻同時(shí)開(kāi)始執(zhí)行。類似于賽跑,將多個(gè)線程放到起點(diǎn),等待發(fā)令槍響,然后同時(shí)開(kāi)跑。做法是初始化一個(gè)共享的CountDownLatch(1),將其計(jì)算器初始化為1,多個(gè)線程在開(kāi)始執(zhí)行任務(wù)前首先countdownlatch.await(),當(dāng)主線程調(diào)用countDown()時(shí),計(jì)數(shù)器變?yōu)?,多個(gè)線程同時(shí)被喚醒。
/**
 * @Description: 模擬100米跑步,5名選手都準(zhǔn)備好了,只等裁判員一聲令下,所有人同時(shí)開(kāi)跑
 */
public class CountDownLatchDemo2 {

    public static void main(String[] args) throws InterruptedException {
        CountDownLatch countDownLatch = new CountDownLatch(1);

        ExecutorService executorService = Executors.newFixedThreadPool(5);
        for (int i = 0; i < 5; i++) {
            final int no = i+1;
            Runnable runnable = new Runnable() {
                @Override
                public void run() {
                    System.out.println("No." + no + ",準(zhǔn)備完畢,等待發(fā)令槍");
                    try {
                        countDownLatch.await();
                        System.out.println("No." + no + ",開(kāi)始跑步");
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            };
            executorService.submit(runnable);
        }

        //裁判員檢查發(fā)令槍......
        Thread.sleep(5000);
        System.out.println("發(fā)令槍響,比賽開(kāi)始......");
        countDownLatch.countDown();
    }
}

CountDownLatch兩種用法結(jié)合使用

/**
 * @Description: 模擬100米跑步,5名選手都準(zhǔn)備好了,只等裁判員一聲令下,所有人同時(shí)開(kāi)跑
 *                  當(dāng)所有人都到終點(diǎn)后,比賽結(jié)束
 */
public class CountDownLatchDemo3 {

    public static void main(String[] args) throws InterruptedException {
        CountDownLatch begin = new CountDownLatch(1);
        CountDownLatch end = new CountDownLatch(5);

        ExecutorService executorService = Executors.newFixedThreadPool(5);
        for (int i = 0; i < 5; i++) {
            final int no = i+1;
            Runnable runnable = new Runnable() {
                @Override
                public void run() {
                    System.out.println("No." + no + ",準(zhǔn)備完畢,等待發(fā)令槍");
                    try {
                        begin.await();
                        System.out.println("No." + no + ",開(kāi)始跑步");
                        Thread.sleep(new Random().nextInt(10000));
                        System.out.println("No." + no + ",跑到終點(diǎn)了");
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }finally {
                        end.countDown();
                    }
                }
            };
            executorService.submit(runnable);
        }

        //裁判員檢查發(fā)令槍......
        Thread.sleep(5000);
        System.out.println("發(fā)令槍響,比賽開(kāi)始......");
        begin.countDown();

        //等待5個(gè)線程都執(zhí)行完畢之后
        end.await();
        System.out.println("所有人到達(dá)終點(diǎn),比賽結(jié)束");
    }
}

CountDownLatch注意點(diǎn)

  • 擴(kuò)展用法:多個(gè)線程等待多個(gè)線程完成執(zhí)行后,再同時(shí)執(zhí)行
  • CountDownLatch是不能夠重用的,如果需要重新計(jì)數(shù),可以考慮使用CyclicBarrier或者創(chuàng)建新的CountDownLatch實(shí)例

Semaphore

Semaphore是一種在多線程環(huán)境下使用的設(shè)施,該設(shè)施負(fù)責(zé)協(xié)調(diào)各個(gè)線程,以保證它們能夠正確、合理的使用公共資源的設(shè)施,也是操作系統(tǒng)中用于控制進(jìn)程同步互斥的量。Semaphore是一種計(jì)數(shù)信號(hào)量,用于管理一組資源,內(nèi)部是基于AQS的共享模式。它相當(dāng)于給線程規(guī)定一個(gè)量從而控制允許活動(dòng)的線程數(shù)。

Semaphore(信號(hào)量)是用來(lái)控制同時(shí)訪問(wèn)特定資源的線程數(shù)量,它通過(guò)協(xié)調(diào)各個(gè)線程,以保證合理的使用公共資源。很多年以來(lái),我都覺(jué)得從字面上很難理解Semaphore所表達(dá)的含義,只能把它比作是控制流量的紅綠燈,比如XX馬路要限制流量,只允許同時(shí)有一百輛車在這條路上行使,其他的都必須在路口等待,所以前一百輛車會(huì)看到綠燈,可以開(kāi)進(jìn)這條馬路,后面的車會(huì)看到紅燈,不能駛?cè)隭X馬路,但是如果前一百輛中有五輛車已經(jīng)離開(kāi)了XX馬路,那么后面就允許有5輛車駛?cè)腭R路,這個(gè)例子里說(shuō)的車就是線程,駛?cè)腭R路就表示線程在執(zhí)行,離開(kāi)馬路就表示線程執(zhí)行完成,看見(jiàn)紅燈就表示線程被阻塞,不能執(zhí)行。

Semaphore 是 synchronized 的加強(qiáng)版,作用是控制線程的并發(fā)數(shù)量。就這一點(diǎn)而言,單純的synchronized 關(guān)鍵字是實(shí)現(xiàn)不了的。

Semaphore主要方法:

  • Semaphore(int permits):構(gòu)造方法,創(chuàng)建具有給定許可數(shù)的計(jì)數(shù)信號(hào)量并設(shè)置為非公平信號(hào)量。
  • Semaphore(int permits,boolean fair):構(gòu)造方法,當(dāng)fair等于true時(shí),創(chuàng)建具有給定許可數(shù)的計(jì)數(shù)信號(hào)量并設(shè)置為公平信號(hào)量,那么Semaphore會(huì)把之前等待的線程放到FIFO的隊(duì)列里,以便于當(dāng)有了新的許可證時(shí),可以分發(fā)給之前等了最長(zhǎng)時(shí)間的線程。
  • void acquire():從此信號(hào)量獲取一個(gè)許可前線程將一直阻塞。相當(dāng)于一輛車占了一個(gè)車位。
  • void acquire(int n):從此信號(hào)量獲取給定數(shù)目許可,在提供這些許可前一直將線程阻塞。比如n=2,就相當(dāng)于一輛車占了兩個(gè)車位。
  • void acquireUninterruptibly():作用是使等待進(jìn)入acquire()方法的線程,不允許被中斷。acquireUninterruptibly()還有重載的方法acquireUninterruptibly(int permits),此方法的作用是在等待permits的情況下不允許被中斷,如果成功獲得鎖,則取得指定permits的個(gè)數(shù)。
  • boolean tryAcquire():從信號(hào)量嘗試獲取一個(gè)許可,如果無(wú)可用許可,直接返回false,不會(huì)阻塞
  • boolean tryAcquire(int permits): 嘗試獲取指定數(shù)目的許可,如果無(wú)可用許可直接返回false
  • boolean tryAcquire(int permits, long timeout, TimeUnit unit): 在指定的時(shí)間內(nèi)嘗試從信號(hào)量中獲取許可,如果在指定的時(shí)間內(nèi)獲取成功,返回true,否則返回false
  • void release():釋放一個(gè)許可,別忘了在finally中使用,注意:多次調(diào)用該方法,會(huì)使信號(hào)量的許可數(shù)增加,達(dá)到動(dòng)態(tài)擴(kuò)展的效果,如:初始permits為1, 調(diào)用了兩次release,最大許可會(huì)改變?yōu)?
  • void release(int n):釋放n個(gè)許可。
  • int availablePermits():當(dāng)前可用的許可數(shù)。
/**
 * @Description: 演示Semaphore用法
 */
public class SemaphoreDemo {

    public static Semaphore semaphore = new Semaphore(3,true);

    public static void main(String[] args) {
        ExecutorService executorService = Executors.newFixedThreadPool(50);

        for (int i = 0; i < 100; i++) {
            executorService.execute(new Task());
        }
        executorService.shutdown();
    }

    static class Task implements Runnable{

        @Override
        public void run() {
            try {
                semaphore.acquire();
                System.out.println(Thread.currentThread().getName()+"拿到了許可證");
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }finally {
                System.out.println(Thread.currentThread().getName()+"釋放了許可證");
                semaphore.release();
            }
        }
    }
}

Semaphore特殊用法

  • 一次性獲取或釋放多個(gè)許可證
    • 比如TaskA會(huì)調(diào)用很消耗資源的method1(),而TaskB調(diào)用的是不太消耗資源的method2(),假設(shè)我們一共有5個(gè)許可證,那么我們可以要求TaskA獲取5個(gè)許可證才能執(zhí)行,而TaskB只需要獲取到一個(gè)許可證就能執(zhí)行,這樣就避免了A和B同時(shí)運(yùn)行的情況,我們可以根據(jù)自己的需求合理分配資源

注意點(diǎn)

  • 獲取和釋放的許可證數(shù)量必須一致,否則,比如每次都獲取2個(gè)但是只是放一個(gè)甚至不釋放,那么隨著時(shí)間的推移,到最后許可證不夠用,會(huì)導(dǎo)致程序卡死。(雖然信號(hào)量類并不對(duì)釋放和獲取的數(shù)量做規(guī)定,但是這是我們的編程規(guī)范,否則容易出錯(cuò))
  • 注意在初始化Semaphore的時(shí)候設(shè)置公平性,一般設(shè)置為true更為合理
  • 并不是必須由獲取許可證的線程來(lái)釋放許可證,事實(shí)上,獲取和釋放許可證對(duì)線程并無(wú)要求,也許是A獲取了,然后由B釋放,只要邏輯合理即可
  • 信號(hào)量的作用,除了控制臨界區(qū)最多同時(shí)有N個(gè)線程訪問(wèn)外,另一個(gè)作用是可以實(shí)現(xiàn)“條件等待”,例如線程1需要在線程2完成準(zhǔn)備工作后才能開(kāi)始工作,那么線程1acquire(),而線程2完成任務(wù)后release(),這樣的話相當(dāng)于輕量級(jí)的CountDownLatch

Condition接口(又稱條件對(duì)象)

在使用Lock之前,我們使用的最多的同步方式應(yīng)該是synchronized關(guān)鍵字來(lái)實(shí)現(xiàn)同步方式了。配合Object的wait()、notify()系列方法可以實(shí)現(xiàn)等待/通知模式。Condition接口也提供了類似Object的監(jiān)視器方法,與Lock配合可以實(shí)現(xiàn)等待/通知模式,但是這兩者在使用方式以及功能特性上還是有差別的。Object和Condition接口的一些對(duì)比。摘自《Java并發(fā)編程的藝術(shù)》


首先我們需要明白condition對(duì)象是依賴于lock對(duì)象的,意思就是說(shuō)condition對(duì)象需要通過(guò)lock對(duì)象進(jìn)行創(chuàng)建出來(lái)(調(diào)用Lock對(duì)象的newCondition()方法)。condition的使用方式非常的簡(jiǎn)單。但是需要注意在調(diào)用方法前獲取鎖。

condition是對(duì)線程進(jìn)行控制管理的接口,具體實(shí)現(xiàn)是AQS的一個(gè)內(nèi)部類ConditionObject,主要功能是控制線程的啟/停(這么說(shuō)并不嚴(yán)格,還要有鎖的競(jìng)爭(zhēng)排隊(duì))。

Condition作用

  • 當(dāng)線程1需要等待某個(gè)條件的時(shí)候,它就去執(zhí)行condition.await()方法,一旦執(zhí)行了await()方法,線程就進(jìn)入了阻塞狀態(tài)
  • 然后通常會(huì)有另外一個(gè)線程,假設(shè)是線程2去執(zhí)行對(duì)應(yīng)的條件,直到這個(gè)條件達(dá)成的時(shí)候,線程2就會(huì)執(zhí)行condition.signal()方法,這時(shí)JVM就會(huì)從被阻塞的線程里找到那些等待該condition的線程,那么當(dāng)線程1收到可執(zhí)行信號(hào)的時(shí)候,它的線程狀態(tài)就會(huì)變成Runnable可執(zhí)行狀態(tài)


signalAll()和signal()區(qū)別

  • signalAll()會(huì)喚起所有的等待線程
  • 但signal()是公平的,只會(huì)喚起那個(gè)等待時(shí)間最長(zhǎng)的線程

Condition基本用法

/**
 * @Description: 演示Condition的基本用法
 */
public class ConditionDemo1 {

    public ReentrantLock lock = new ReentrantLock();

    public Condition condition = lock.newCondition();

    public void method1(){
        lock.lock();
        try{
            System.out.println("條件不滿足,開(kāi)始await");
            condition.await();
            System.out.println("條件滿足,開(kāi)始執(zhí)行后續(xù)任務(wù)");
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }

    public void method2(){
        lock.lock();
        try{
            System.out.println("準(zhǔn)備工作完成,開(kāi)始喚醒其他線程");
            condition.signal();
        }finally {
            lock.unlock();
        }
    }

    public static void main(String[] args) {
        ConditionDemo1 conditionDemo1 = new ConditionDemo1();

        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    Thread.sleep(1000);
                    conditionDemo1.method2();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }).start();

        conditionDemo1.method1();
    }
}

Condition實(shí)現(xiàn)生產(chǎn)者和消費(fèi)者模式

/**
 * @Description: 演示Condition實(shí)現(xiàn)生產(chǎn)者和消費(fèi)者模式
 */
public class ConditionDemo2 {

    private int queueSize = 10;
    private PriorityQueue<Integer> queue = new PriorityQueue<>(queueSize);

    private ReentrantLock lock = new ReentrantLock();
    private Condition notFull = lock.newCondition();
    private Condition notEmpty = lock.newCondition();

    public static void main(String[] args) {
        ConditionDemo2 conditionDemo2 = new ConditionDemo2();
        Producer producer = conditionDemo2.new Producer();
        Consumer consumer = conditionDemo2.new Consumer();

        new Thread(producer).start();
        new Thread(consumer).start();
    }

    class Consumer implements Runnable{
        @Override
        public void run() {
            consume();
        }

        public void consume(){
            while(true){
                lock.lock();
                try{
                    while (queue.size() == 0){
                        System.out.println("隊(duì)列空,等待數(shù)據(jù)");
                        notEmpty.await();
                    }
                    Integer poll = queue.poll();//走過(guò)await()證明隊(duì)列不為空,取出數(shù)據(jù)
                    System.out.println("消費(fèi)者消費(fèi)數(shù)據(jù):"+poll+",隊(duì)列剩余數(shù)據(jù)數(shù)量:"+queue.size());

                    notFull.signalAll();//獲取數(shù)據(jù)之后,隊(duì)列肯定有空閑,那么喚醒生產(chǎn)者進(jìn)行生產(chǎn)
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    lock.unlock();
                }
            }
        }
    }

    class Producer implements Runnable{
        @Override
        public void run() {
            produce();
        }

        public void produce(){
            while(true){
                lock.lock();
                try{
                    while (queue.size() == queueSize){
                        System.out.println("隊(duì)列已滿,等待空余");
                        notFull.await();
                    }
                    queue.offer(1);//走過(guò)await()證明隊(duì)列有空閑,開(kāi)始往隊(duì)列里生產(chǎn)數(shù)據(jù)
                    System.out.println("生產(chǎn)者向隊(duì)列生產(chǎn)一個(gè)數(shù)據(jù),隊(duì)列剩余空間:"+(queueSize-queue.size()));

                    notEmpty.signalAll();//向隊(duì)列生產(chǎn)數(shù)據(jù)之后,隊(duì)列不為空,那么喚醒消費(fèi)者進(jìn)行消費(fèi)
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    lock.unlock();
                }
            }
        }
    }
}

Condition注意點(diǎn)

  • 實(shí)際上,如果說(shuō)Lock用來(lái)替代synchronized,那么Condition就是用來(lái)代替相對(duì)應(yīng)的Object.wait/notify的,所以在用法和性質(zhì)上,幾乎一樣
  • await方法會(huì)自動(dòng)釋放持有的Lock鎖,和Object.wait一樣,不需要自己手動(dòng)先釋放鎖,而sleep()并不釋放鎖。
  • 調(diào)用await的時(shí)候,必須持有鎖,否則會(huì)拋異常,和Object.wait一樣

CyclicBarrier循環(huán)柵欄

  • CyclicBarrier是java.util.concurrent包下面的一個(gè)工具類,字面意思是可循環(huán)使用(Cyclic)的屏障(Barrier),通過(guò)它可以實(shí)現(xiàn)讓一組線程到達(dá)一個(gè)屏障(也可以叫同步點(diǎn))時(shí)被阻塞,直到最后一個(gè)線程到達(dá)屏障時(shí),所有被屏障攔截的線程才會(huì)繼續(xù)執(zhí)行。

  • CyclicBarrier循環(huán)柵欄和CountDownLatch很類似,都能阻塞一組線程

  • 柵欄類似于閉鎖,它能阻塞一組線程直到某個(gè)事件的發(fā)生。柵欄與閉鎖的關(guān)鍵區(qū)別在于,所有的線程必須同時(shí)到達(dá)柵欄位置,才能繼續(xù)執(zhí)行。閉鎖用于等待事件,而柵欄用于等待其他線程。

  • CyclicBarrier可以使一定數(shù)量的線程反復(fù)地在柵欄位置處匯集。當(dāng)線程到達(dá)柵欄位置時(shí)將調(diào)用await方法,這個(gè)方法將阻塞直到所有線程都到達(dá)柵欄位置。如果所有線程都到達(dá)柵欄位置,那么柵欄將打開(kāi),此時(shí)所有的線程都將被釋放,而柵欄將被重置以便下次使用。

CyclicBarrier支持一個(gè)可選的Runnable命令,每個(gè)屏障點(diǎn)運(yùn)行一次,在派對(duì)中的最后一個(gè)線程到達(dá)之后,但在任何線程釋放之前。 在任何一方繼續(xù)進(jìn)行之前,此屏障操作對(duì)更新共享狀態(tài)很有用。

實(shí)現(xiàn)原理:在CyclicBarrier的內(nèi)部定義了一個(gè)Lock對(duì)象,每當(dāng)一個(gè)線程調(diào)用await方法時(shí),將攔截的線程數(shù)減1,然后判斷剩余攔截?cái)?shù)是否為初始值parties,如果不是,進(jìn)入Lock對(duì)象的條件隊(duì)列等待。如果是,執(zhí)行barrierAction對(duì)象的Runnable方法,然后將鎖的條件隊(duì)列中的所有線程放入鎖等待隊(duì)列中,這些線程會(huì)依次的獲取鎖、釋放鎖。

CyclicBarrier構(gòu)造方法

  • CyclicBarrier(int parties)
    創(chuàng)建一個(gè)新的 CyclicBarrier ,當(dāng)給定數(shù)量的線程(線程)等待它時(shí),它將跳閘,并且當(dāng)屏障跳閘時(shí)不執(zhí)行預(yù)定義的動(dòng)作。
  • CyclicBarrier(int parties, Runnable barrierAction)
    創(chuàng)建一個(gè)新的 CyclicBarrier ,當(dāng)給定數(shù)量的線程(線程)等待時(shí),它將跳閘,當(dāng)屏障跳閘時(shí)執(zhí)行給定的屏障動(dòng)作,由最后一個(gè)進(jìn)入屏障的線程執(zhí)行。

CyclicBarrier方法

  • int await() 等待所有 parties已經(jīng)在這個(gè)障礙上調(diào)用了 await 。
  • int await(long timeout, TimeUnit unit) 等待所有 parties已經(jīng)在此屏障上調(diào)用 await ,或指定的等待時(shí)間過(guò)去。
  • int getNumberWaiting() 返回目前正在等待障礙的各方的數(shù)量。
  • int getParties() 返回旅行這個(gè)障礙所需的parties數(shù)量。
  • boolean isBroken() 查詢這個(gè)障礙是否處于破碎狀態(tài)。
  • void reset() 將屏障重置為初始狀態(tài)。

CyclicBarrier的用法

/**
 * @Description: 演示CyclicBarrier的使用
 */
public class CyclicBarrierDemo {

    public static void main(String[] args) {
        CyclicBarrier cyclicBarrier = new CyclicBarrier(5, new Runnable() {
            @Override
            public void run() {
                System.out.println("所有人都到場(chǎng)了,大家統(tǒng)一出發(fā)");
            }
        });
        for (int i = 0; i < 10; i++) {
            new Thread(new Task(i,cyclicBarrier)).start();
        }
    }

    static class Task implements Runnable{

        private int id;

        private CyclicBarrier cyclicBarrier;

        public Task(int id, CyclicBarrier cyclicBarrier) {
            this.id = id;
            this.cyclicBarrier = cyclicBarrier;
        }

        @Override
        public void run() {
            System.out.println(Thread.currentThread().getName()+",id:"+id+"現(xiàn)前往集合地點(diǎn)");
            try {
                Thread.sleep(new Random().nextInt(10000));
                System.out.println(Thread.currentThread().getName()+"到了集合地點(diǎn),開(kāi)始等待其他人到達(dá)");
                cyclicBarrier.await();
                System.out.println(Thread.currentThread().getName()+"出發(fā)了");
            } catch (InterruptedException e) {
                e.printStackTrace();
            } catch (BrokenBarrierException e) {
                e.printStackTrace();
            }
        }
    }
}

CyclicBarrier和CountDownLatch的區(qū)別

  • 作用不同:CyclicBarrier要等固定數(shù)量的線程都到達(dá)了柵欄位置才能繼續(xù)執(zhí)行,而CountDownLatch只需要等待數(shù)字到0,也就是說(shuō),CountDownLatch用于事件,CyclicBarrier是用于線程的
  • 可重用性不同:CountDownLatch在倒數(shù)到0并觸發(fā)門(mén)閂打開(kāi)后,就不能再次使用了,除非新建實(shí)例;而CyclicBarrier可以重復(fù)使用
  • CountDownLatch是減計(jì)數(shù)方式,而CyclicBarrier是加計(jì)數(shù)方式。
  • CountDownLatch計(jì)數(shù)為0無(wú)法重置,而CyclicBarrier計(jì)數(shù)達(dá)到初始值,則可以重置。

參考:
https://www.cnblogs.com/Lee_xy_z/p/10470181.html

https://www.cnblogs.com/wxgblogs/p/5422508.html

https://www.cnblogs.com/gemine/p/9039012.html

https://www.cnblogs.com/brokencolor/p/9752941.html

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容