常用辅助类(ContDownLatch、CyclicBarrier、Semaphore)

1. ContDownLatch(倒计时器)

ContDownLatch是一个同步辅助类,在完成某些运算时,只有其他所有线程的运算全部完成,当前运算才继续执行,这就叫闭锁。

● CountDownLatch(int count):实例化一个倒计数器,count指定计数个数

● countDown():计数减一

● await():等待,当计数减到0时,所有线程并行执行

代码演示:火箭发射

public class ConutDownLatchDemo {
    public static void main(String[] args) throws InterruptedException {

        // 倒计时器:计数数量为10
        CountDownLatch latch = new CountDownLatch(10);        // 倒计时检查任务,使用10个线程来完成任务
        CountRunnable runnable = new CountRunnable(latch);
        for (int i = 0; i < 10; i++) {
            new Thread(runnable).start(); // 执行任务
        }
        // 等待检查完成
        latch.await();
        // 发射火箭
        System.out.println("Fire!");
    }
}

class CountRunnable implements Runnable {

    private CountDownLatch countDownLatch;

    public CountRunnable(CountDownLatch countDownLatch) {
        this.countDownLatch = countDownLatch;
    }

    @Override
    public void run() {
        // 模拟检查任务
        try {
            Thread.sleep(new Random().nextInt(10) * 1000);
            System.out.println("check complete");
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            // 计数 -1
            // 放在finally避免任务执行过程出现异常,导致countDown()不能被执行
            countDownLatch.countDown();
        }
    }
}

上述代码中我们先生成了一个CountDownLatch实例。计数数量为10,这表示需要有10个线程来完成任务,等待在CountDownLatch上的线程才能继续执行。latch.countDown();方法作用是通知CountDownLatch有一个线程已经准备完毕,倒计数器可以减一了。latch.await()方法要求主线程等待所有10个检查任务全部准备好才一起并行执行。

2. CyclicBarrier(加法计数器)

一个同步辅助类,它允许一组线程互相等待,直到到达某个公共屏障点 (common barrier point)。在涉及一组固定大小的线程的程序中,这些线程必须不时地互相等待,此时 CyclicBarrier 很有用。因为该 barrier 在释放等待线程后可以重用,所以称它为循环 的 barrier。

使用场景

需要所有的子任务都完成时,才执行主任务,这个时候就可以选择使用CyclicBarrier。

代码演示:集龙珠召唤神龙

public class CyclicBarrierDemo {
    public static void main(String[] args) {

        // 加法计数器,集齐7颗龙珠召唤神龙
        CyclicBarrier cyclicBarrier = new CyclicBarrier(7, new Runnable() {
            @Override
            public void run() {
                System.out.println("集齐龙珠 -> 召唤神龙!嗷!嗷!嗷!!!");
            }
        });

        for (int i = 0; i < 7; i++) {
            new Thread(() -> {
                System.out.println("获得(" + Thread.currentThread().getName() + ")星龙珠");
                try {
                    cyclicBarrier.await(); // 等待其它线程执行完成
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } catch (BrokenBarrierException e) {
                    e.printStackTrace();
                }
            }, String.valueOf(i + 1)).start();
        }
    }
}

◇  CountDownLatch 和 CyclicBarrier 比较

① CountDownLatch 是线程组之间的等待,即一个(或多个)线程等待N个线程完成某件事情之后再执行。

  CyclicBarrier 则是线程组内的等待,即每个线程相互等待,即 N 个线程都被拦截之后,然后依次执行。

② CountDownLatch 是减计数方式。

  CyclicBarrier 是加计数方式。

③ CountDownLatch 计数为 0 无法重置。

  CyclicBarrier 计数达到初始值,则可以重置。

④ CountDownLatch 不可以复用,

  CyclicBarrier 可以复用。

3. Semaphore(信号量)

Semaphore 是一个计数信号量,必须由获取它的线程释放。

作用:多个共享资源互斥的使用!并发限流,限制可以访问某些资源的线程数量。

Semaphore 只有3个操作:① 初始化 ② 增加 ③ 减少

常用方法

public void  acquire();// 获取一个许可
public void  acquire(intpermits);// 获取permits个许可
public void  release();// 释放一个许可
public void  release(intpermits);// 释放permits个许可

- acquire:用来获取一个许可,若无许可能够获得,则会一直等待,直到获得许可。

- release:用来释放许可。注意,在释放许可之前,必须先获获得许可。

这4个方法都会被阻塞,如果想立即得到执行结果,可以使用下面几个方法:

// 尝试获取一个许可,若获取成功,则立即返回true,若获取失败,则立即返回false
public  boolean  tryAcquire();

// 尝试获取一个许可,若在指定的时间内获取成功,则立即返回true,否则则立即返回false
public  boolean  tryAcquire(longtimeout, TimeUnit unit);

// 尝试获取permits个许可,若获取成功,则立即返回true,若获取失败,则立即返回false
public  boolean  tryAcquire(intpermits);

// 尝试获取permits个许可,若在指定的时间内获取成功,则立即返回true,否则则立即返回false
public  boolean  tryAcquire(intpermits, longtimeout, TimeUnit unit);

代码演示:抢车位

现有6辆车,但是只有3个停车位

public class SemaphoreDemo {
    public static void main(String[] args) {

        // 信号量,只允许 3 个线程同时访问
        Semaphore semaphore = new Semaphore(3);

        for (int i = 0; i < 6; i++) {
            new Thread(() -> {
                try {
                    semaphore.acquire(); // 获取许可,将信号量 +1,如果信号量已经满了,等待被释放为止!
                    System.out.println("Car" + Thread.currentThread().getName() + " >>> 抢到车位");
                    TimeUnit.SECONDS.sleep(new Random().nextInt(10));
                    System.out.println("离开车位 >>> Car" + Thread.currentThread().getName());
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    semaphore.release(); // 释放,将信号量 -1,然后唤醒等待的线程!
                }
            }, String.valueOf(i)).start();
        }
    }
}

◇  总结

CountDownLatch 是一个线程等待其他线程, CyclicBarrier 是多个线程互相等待。

CountDownLatch 的计数是减 1 直到 0,CyclicBarrier 是加 1,直到指定值。

CountDownLatch 是一次性的, CyclicBarrier 可以循环利用。

CyclicBarrier 可以在最后一个线程达到屏障之前,选择先执行一个操作。

Semaphore ,需要拿到许可才能执行,并可以选择公平和非公平模式。

原文地址:https://www.cnblogs.com/Dm920/p/13336381.html