CountDownLatch、Semaphore、CyclicBarrier应用与解析

CountDownLatch

CountDownLatch能够实现让某一个线程等待其他线程执行完成后再执行,比如多个线程负责计算各自的数据,数据之间没有联系,一个线程等待所有线程都计算完后进行数据的汇总。

传统的串行执行的方式,假设线程A/B/C分别进行计算,再由D线程汇总数据,总共耗时7秒。

但是如果A/B/C计算之间并没有任何依赖关系,那么就可以让他们同时进行计算,线程D等待所有线程都执行完之后再进行汇总,这样总共耗时就只需4秒。

当然除了CountDownLatch,还有其他的一些方法也能实现类似的效果,本文就只讨论CountDownLatch提供的方式。
在这里插入图片描述
CountDownLatch是通过计数器的方式实现的,构建CountDownLatch时先定义一个数量,比如3,然后线程D开始等待,每当线程A/B/C有一个完成任务时就减1,当计数器被减到0时,表示A/B/C任务都已执行完毕,则原先等待的线程D继续执行后续的任务。

public class CountDownLatchDemo {

    //定义计数器为3
    static CountDownLatch latch = new CountDownLatch(3);

    public static void main(String[] args) throws InterruptedException {
        new Thread(() -> {
            System.out.println("线程A执行耗时1秒。。。");
            try {
                Thread.sleep(1000);
                //计数器减1
                latch.countDown();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }).start();

        new Thread(() -> {
            System.out.println("线程B执行耗时2秒。。。");
            try {
                Thread.sleep(2000);
                //计数器减1
                latch.countDown();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }).start();

        new Thread(() -> {
            System.out.println("线程C执行耗时3秒。。。");
            try {
                Thread.sleep(3000);
                //计数器减1
                latch.countDown();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }).start();

        long s = System.currentTimeMillis();
        //阻塞当前线程,直到计数器为0时释放
        latch.await();
        long e = System.currentTimeMillis();
        System.out.println("总共耗时:" + (e - s) + "毫秒");
    }
}
线程A执行耗时1秒。。。
线程B执行耗时2秒。。。
线程C执行耗时3秒。。。
总共耗时:3000毫秒

CountDownLatch除了实现上述类似的场景之外,还可以实现模拟并发的功能。

public class CountDownLatchConcurrentDemo {

    //控制并发
    static CountDownLatch lock = new CountDownLatch(1);

    //控制10个线程全部准备好
    static CountDownLatch countDownLatch = new CountDownLatch(10);

    public static void main(String[] args) throws InterruptedException {
        for (int i = 0; i < 10; i++) {
            new Thread(() -> {
                try {
                    //创建完一个线程计数器扣减1,直到10个线程全部创建完成
                    countDownLatch.countDown();
                    //等待控制并发的计数器为0
                    lock.await();
                    System.out.println("thread name: " + Thread.currentThread().getName() + " 当前时间:" + System.currentTimeMillis());
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }).start();
        }
        //阻塞等待10个线程准备好
        countDownLatch.await();
        //释放锁,10个线程同时执行后续业务
        lock.countDown();
    }
}
thread name: Thread-0 当前时间:1593094595528
thread name: Thread-7 当前时间:1593094595528
thread name: Thread-1 当前时间:1593094595528
thread name: Thread-3 当前时间:1593094595528
thread name: Thread-9 当前时间:1593094595528
thread name: Thread-8 当前时间:1593094595528
thread name: Thread-4 当前时间:1593094595528
thread name: Thread-6 当前时间:1593094595528
thread name: Thread-2 当前时间:1593094595528
thread name: Thread-5 当前时间:1593094595528

CyclicBarrier

CyclicBarrier意为可循环屏障,多个线程到达屏障点时阻塞,直到最后一个线程到达则开启屏障,然后释放所有阻塞的线程一起执行。

public class CyclicBarrierDemo {

    private static CyclicBarrier barrier = new CyclicBarrier(3);

    public static void main(String[] args) {
        for (int i = 0; i < 3; i++) {
            new Thread(() -> {
                System.out.println(Thread.currentThread().getName() + "线程到达,开始等待!");
                try {
                    barrier.await();
                    System.out.println(Thread.currentThread().getName() + "线程进入!");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } catch (BrokenBarrierException e) {
                    e.printStackTrace();
                }
            }).start();
        }
    }
}
Thread-0线程到达,开始等待!
Thread-2线程到达,开始等待!
Thread-1线程到达,开始等待!
Thread-1线程进入!
Thread-0线程进入!
Thread-2线程进入!

CyclicBarrier还有一个带Runnable参数的构造方法,表示所有线程到达屏障后,优先执行Runnable。

public class CyclicBarrierDemo {

    private static CyclicBarrier barrier = new CyclicBarrier(3, () -> {
        try {
        	//休眠了1秒,证明runnable是优先执行
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("构造方法中的线程。。。");
    });

    public static void main(String[] args) {
        for (int i = 0; i < 3; i++) {
            new Thread(() -> {
                System.out.println(Thread.currentThread().getName() + "线程到达,开始等待!");
                try {
                    barrier.await();
                    System.out.println(Thread.currentThread().getName() + "线程进入!");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } catch (BrokenBarrierException e) {
                    e.printStackTrace();
                }
            }).start();
        }
    }
}
Thread-0线程到达,开始等待!
Thread-2线程到达,开始等待!
Thread-1线程到达,开始等待!
构造方法中的线程。。。
Thread-1线程进入!
Thread-0线程进入!
Thread-2线程进入!

CyclicBarrier也是可应用与多个线程计算,然后由一个线程等待并最终合并结果处理的场景。

CountDownLatch和CyclicBarrier区别
1、他们都是利用计数器来实现对线程的控制,CountDownLatch只能使用一次,CyclicBarrier可循环使用。

2、CountDownLatch需要由线程调用countDown方法扣减计数,CyclicBarrier直接调用await就会阻塞并且扣减计数。

3、要实现汇总结算的方式,CountDownLatch主线程等待await即可,
CyclicBarrier则通过使用带Runnable的构造方法实现。

4、要实现模拟并发,需要构建2个CountDownLatch,CyclicBarrier则更方便,每个线程各自调用await,最后一个线程调用完,则所有线程就会同时执行之后的任务。

Semaphore

Semaphore信号量,用来控制访问流量,经常应用在限流的场景中

Semaphore的构造方法Semaphore(int permits)传入一个int型数字,表示许可证的数量。

public class SemaphoreDemo {
    public static void main(String[] args) {
        //5个许可证
        Semaphore semaphore = new Semaphore(5);
        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                try {
                    //acquire表示领取一个许可证,10个线程需要10个许可证。
                    //一次最多只能领取到5个,必须等有许可证归还才能继续领取。
                    semaphore.acquire();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("领取一个许可证,当前可用的许可证数量:" + semaphore.availablePermits());
            }
        }).start();

        new Thread(() -> {
            for (int i = 0; i < 5; i++) {
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                //release表示归还一个许可证
                semaphore.release();
                System.out.println("******归还一个许可证,当前可用的许可证数量:" + semaphore.availablePermits());
            }
        }).start();
    }
}
领取一个许可证,当前可用的许可证数量:4
领取一个许可证,当前可用的许可证数量:3
领取一个许可证,当前可用的许可证数量:2
领取一个许可证,当前可用的许可证数量:1
领取一个许可证,当前可用的许可证数量:0
******归还一个许可证,当前可用的许可证数量:1
领取一个许可证,当前可用的许可证数量:0
******归还一个许可证,当前可用的许可证数量:1
领取一个许可证,当前可用的许可证数量:0
******归还一个许可证,当前可用的许可证数量:1
领取一个许可证,当前可用的许可证数量:0
******归还一个许可证,当前可用的许可证数量:1
领取一个许可证,当前可用的许可证数量:0
******归还一个许可证,当前可用的许可证数量:1
领取一个许可证,当前可用的许可证数量:0
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
CountDownLatchCyclicBarrierSemaphore都是Java中用于多线程编程的同步工具类,但它们有不同的作用和使用场景。 1. CountDownLatch CountDownLatch(倒计时门闩)是一个同步工具类,它允许一个或多个线程等待其他线程完成操作后再继续执行。CountDownLatch维护了一个计数器,初始值为线程数量,线程完成任务后计数器减1。当计数器为0时,等待线程继续执行。CountDownLatch的主要方法是await()和countDown()。 使用场景:CountDownLatch适用于一组线程等待另一组线程完成操作后再继续执行的场景。比如,主线程等待所有子线程完成初始化后再继续执行。 2. CyclicBarrier CyclicBarrier(循环屏障)是一个同步工具类,它允许一组线程相互等待,直到所有线程都到达某个屏障点后再继续执行。CyclicBarrier的主要方法是await()。 使用场景:CyclicBarrier适用于需要多个线程协同完成某个任务的场景。比如,多个线程同时执行某个操作,需要等待所有线程都执行完后再进行下一步操作。 3. Semaphore Semaphore(信号量)是一个同步工具类,它允许多个线程同时访问某个资源。Semaphore维护了一个许可证数量,线程需要先获取许可证才能访问资源,访问完后释放许可证。Semaphore的主要方法是acquire()和release()。 使用场景:Semaphore适用于需要限制线程数量访问某个资源的场景。比如,数据库连接池限制同时访问连接的线程数量。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

码拉松

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值