java 中 CountDownLatch、CyclicBarrier 和 Semaphore 的简单使用

54 阅读2分钟

一、CountDownLatch

1.1 概述

    让一些线程阻塞直到另外一些完成后才被唤醒。

    该类主要有两个方法,当一个或多个线程调用await方法时,调用线程会被阻塞。其他线程调用 countDown方法计数器减1(调用countDown方法时线程不会阻塞),当计数器的值变为0,因调用await方法被阻塞的线程会被唤醒继续执行。

1.2 模拟场景

    有 6 名学生上完课后,离开教室,但他们离开的时间不相同,班长需要最后一个离开,锁上教室的门。

1.3 程序模拟

1.3.1 使用 CountDownLatch 之前

public void closeDoor() {

    for (int i = 1; i <= 6; i++) {
        new Thread(() -> {
            System.out.println(Thread.currentThread().getName() + " 离开教室");
        }, String.valueOf(i)).start();
    }

    System.out.println(Thread.currentThread().getName() + " 班长锁门离开教室");
}

image.png

1.3.2 使用 CountDownLatch 之后

public void closeDoor() throws InterruptedException {
    CountDownLatch countDownLatch = new CountDownLatch(6);

    for (int i = 1; i <= 6; i++) {
        new Thread(() -> {
            System.out.println(Thread.currentThread().getName() + " 离开教室");
            countDownLatch.countDown();
        }, String.valueOf(i)).start();
    }

    countDownLatch.await();
    System.out.println(Thread.currentThread().getName() + " 班长锁门离开教室");
}

image.png

1.4 CountDownLatch 配合枚举类的使用

private static void nationalUnification() throws InterruptedException {
    CountDownLatch countDownLatch = new CountDownLatch(6);

    for (int i = 1; i <= 6; i++) {
        new Thread(() -> {
            System.out.println(Thread.currentThread().getName() + "国被灭...");
            countDownLatch.countDown();
        }, CountryEnum.forEachCountryEnum(i).getCountry()).start();
    }

    countDownLatch.await();
    System.out.println(Thread.currentThread().getName() + " 秦国灭六国,一统华夏");
}

// 枚举类,存储国家的名字
public enum CountryEnum {

    ONE(1, "齐"),
    TWO(2, "楚"),
    THREE(3, "燕"),
    FOUR(4, "赵"),
    FIVE(5, "魏"),
    SIX(6, "韩");

    private Integer code;
    private String country;

    CountryEnum(Integer code, String country) {
        this.code = code;
        this.country = country;
    }

    public Integer getCode() {
        return code;
    }

    public String getCountry() {
        return country;
    }

    public static CountryEnum forEachCountryEnum(int index) {
        CountryEnum[] countryEnums = CountryEnum.values();
        for (CountryEnum element : countryEnums) {
            if (element.getCode() == index) {
                return element;
            }
        }
        return null;
    }
}

image.png

二、CyclicBarrier

2.1 概述

    CyclicBarrier的字面意思是可循环(Cyclic),使用的屏障(barrier)。它要做的事情是,让一组线程到达一个屏障(也可以叫做同步点)时被阻塞,直到最后一个线程到达屏障时,屏障才会开门,所有被屏障拦截的线程才会继续干活,线程进入屏障通过CyclicBarrierawait()方法。

2.2 示例

public void meet() {
    CyclicBarrier cyclicBarrier = new CyclicBarrier(10, () -> {
        System.out.println("人齐了,准备开会");
    });

    for (int i = 1; i <= 10; i++) {
        new Thread(() -> {
            System.out.println(Thread.currentThread().getName() + " 来到会议室");
            try {
                cyclicBarrier.await();
            } catch (InterruptedException | BrokenBarrierException e) {
                e.printStackTrace();
            }
        }, String.valueOf(i)).start();
    }
}

image.png

三、Semaphore

3.1 概述

A counting semaphore. Conceptually, a semaphore maintains a set of permits. Each acquire() blocks if necessary until a permit is available, and then takes it. Each release() adds a permit, potentially releasing a blocking acquirer. However, no actual permit objects are used; the Semaphore just keeps a count of the number available and acts accordingly.

计数信号量。从概念上讲,信号量维护一组许可。如有必要,每个acquire()都会阻止,直到许可证可用,然后接受它。每个release()都会添加一个许可证,从而可能释放一个阻塞收单器。但是,没有使用实际的许可对象;信号量只是保留可用数量的计数并采取相应的行动。

3.2 示例

public void semaphoreTest() {
    Semaphore semaphore = new Semaphore(3);

    for (int i = 1; i <= 6; i++) {
        new Thread(() -> {
            try {
                semaphore.acquire();
                System.out.println(Thread.currentThread().getName() + " 抢到车位...");
                TimeUnit.SECONDS.sleep(3);
                System.out.println(Thread.currentThread().getName() + "停车3秒,离开车位");
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                semaphore.release();
            }
        }, String.valueOf(i)).start();
    }
}

image.png