天天看點

Java多線程之CountDownLatch、CyclicBarrier和Semaphore

轉自:http://www.liubey.org/countdownlatch_vs_cyclicbarrier/

概述

CountDownLatch : 一個線程(或者多個), 等待另外N個線程完成某個事情之後才能執行。

CyclicBarrier : N個線程互相等待,任何一個線程完成之前,所有的線程都必須等待。

Semaphore:可以控制某個資源可被同時通路的個數,通過 acquire() 擷取一個許可,如果沒有就等待,而 release() 釋放一個許可。

舉例

體育課時老師拿着秒表測試同學的800米成績,那需求就是很簡單了,老師在起跑處組織大家一起跑的瞬間按下秒表計時開始,然後再終點處等待最後一個學生通過終點後開始彙集學生成績。

1)await(),阻塞等待,直到計數器清零

2)await(int timeout, TimeUnit unit),使線程阻塞,除非被中斷或者超過等待的最大時間

如果達到計數器清零,則await傳回true,如果等待超過了最大的等待時間,則傳回false

3)countDown(),計數器減一,當計數器清零時,await的線程被喚醒,線程繼續執行

4)getCount (),擷取目前計數器的大小

import java.util.concurrent.CountDownLatch;

import java.util.concurrent.ExecutorService;

import java.util.concurrent.Executors;

public class TeacherWithStopwatch {

    public static final int NUMBER_OF_STUDENT = 10;

    public static final int NUMBER_OF_TEACHER = 1;

    public static void main(String[] args) {

        ExecutorService executor = Executors.newFixedThreadPool(2);

        CountDownLatch studentSignal = new CountDownLatch(NUMBER_OF_STUDENT);

        CountDownLatch teacherSignal = new CountDownLatch(NUMBER_OF_TEACHER);

        for (int i = 0; i < NUMBER_OF_STUDENT; i++) {

            executor.execute(new Student(i, studentSignal, teacherSignal));

        }

        try {

            System.out.println("各就各位!開跑!");

            teacherSignal.countDown();

            studentSignal.await();

            System.out.println("結果發送到彙報成績的系統");

        } catch (InterruptedException e) {

            e.printStackTrace();

        } finally {

            executor.shutdown();

    }

}

    import java.util.concurrent.CountDownLatch;

public class Student implements Runnable {

    private int id;

    private CountDownLatch studentSignal;

    private CountDownLatch teacherSignal;

    public Student(int id, CountDownLatch studentSignal,

        CountDownLatch teacherSignal) {

        this.id = id;

        this.studentSignal = studentSignal;

        this.teacherSignal = teacherSignal;

    @Override

    public void run() {

            teacherSignal.await();

            System.out.println("學生" + id + "起跑...");

            System.out.println("學生" + id + "到達終點。");

            studentSignal.countDown();

            System.out.println("學生" + id + "繼續幹其他事情");

有四個遊戲玩家玩遊戲,遊戲有三個關卡,每個關卡必須要所有玩家都到達後才能允許通關。

其實這個場景裡的玩家中如果有玩家A先到了關卡1,他必須等待其他所有玩家都到達關卡1時才能通過。

也就是說線程之間需要互相等待,這和CountDownLatch的應用場景有差別,

CountDownLatch裡的線程是到了運作的目标後繼續幹自己的其他事情,而這裡的線程需要等待其他線程後才能繼續完成下面的工作。

public CyclicBarrier(int parties) 建立一個新的 CyclicBarrier,

它将在給定數量的參與者(線程)處于等待狀态時啟動,但它不會在啟動 barrier 時執行預定義的操作。

public CyclicBarrier(int parties, Runnable barrierAction) 建立一個新的 CyclicBarrier,

它将在給定數量的參與者(線程)處于等待狀态時啟動,并在啟動 barrier 時執行給定的屏障操作,

該操作由最後一個進入 barrier 的線程執行。

public int await() throws InterruptedException, BrokenBarrierException 在所有參與者都已經在此 barrier 上調用 await 方法之前,将一直等待。

public int await(long timeout,TimeUnit unit) throws InterruptedException, BrokenBarrierException,TimeoutException

在所有參與者都已經在此屏障上調用 await 方法之前将一直等待,或者超出了指定的等待時間。

public int getNumberWaiting() 傳回目前在屏障處等待的參與者數目。此方法主要用于調試和斷言。

public int getParties() 傳回要求啟動此 barrier 的參與者數目。

public boolean isBroken() 查詢此屏障是否處于損壞狀态。

public void reset() 将屏障重置為其初始狀态。

public class GameBarrier {

    public static final int NUMBER_OF_PLAYERS = 4;

    ExecutorService executor = Executors.newFixedThreadPool(NUMBER_OF_PLAYERS);

    CyclicBarrier barrier = new CyclicBarrier(NUMBER_OF_PLAYERS, new Runnable() {

        @Override

        public void run() {

            System.out.println("所有玩家通過第一關!");

    });

    for (int i = 0; i < NUMBER_OF_PLAYERS; i++) {

        executor.execute(new Player(i, barrier));

    executor.shutdown();

public class Player implements Runnable {

    private CyclicBarrier cyclicBarrier;

    public Player(int id, CyclicBarrier cyclicBarrier) {

        this.cyclicBarrier = cyclicBarrier;

            System.out.println("玩家" + id + "通過第一關...");

            cyclicBarrier.await();

            System.out.println("玩家" + id + "進入第二關...");

        } catch (BrokenBarrierException e) {

        e.printStackTrace();

一個資源池隻能同時五個人使用,那麼十個人來輪詢使用的情況就是每個人都先申請資源,使用完歸還于下一個人使用。

構造器中的fairness為true時,Semaphore保證各線程以後進先出(FIFO)的方式獲得信号量。如果fairness為false,則不保證這種順序,允許各線程之間的“讨價還價”。

tryAcquire與release為主要方法

public class Person implements Runnable {

    private SemaphorePool pool;

    public Person(int id, SemaphorePool pool) {

        this.pool = pool;

            pool.applyResource();

            System.out.println("人物" + id + "進入");

            Thread.sleep(1000);

            System.out.println("人物" + id + "離開");

            pool.releaseResource();

public class SemaphorePool {

    private Semaphore canExecuteCount = new Semaphore(5, true);

    private static final int TRY_EXECUTE_TIMEOUT = 20;

    public boolean applyResource() {

        boolean canExecute = false;

            canExecute = canExecuteCount.tryAcquire(1, TRY_EXECUTE_TIMEOUT,

                TimeUnit.SECONDS);

        } catch (InterruptedException e) {}

        return canExecute;

    public void releaseResource() {

        canExecuteCount.release(1);

public class TestSemaphore {

    public static final int NUMBER_OF_PERSONS = 10;

    public static final SemaphorePool pool = new SemaphorePool();

        ExecutorService executor = Executors.newFixedThreadPool(NUMBER_OF_PERSONS);

        for(int i=0;i<NUMBER_OF_PERSONS;i++) {

            executor.execute(new Person(i, pool));

        executor.shutdown();

}本文轉自 古道卿 51CTO部落格,原文連結:http://blog.51cto.com/gudaoqing/1550156