CountDownLatch的使用

概要

有时候会遇到一些需要使用多线程的业务场景,比如我现在要获取用户的信息,这些信息存在于不同的中心,需要调用回来组成完整的用户信息,如果采用同步的话,可能会很慢,所以采用异步的方式,最后等所有的子线程调用完成后,主线程才返回,这里我们采用CountDownLatch提供的方法实现线程同步。

CountDownLatch的原理

CountDownLatch(倒计时门闩)是一种多线程同步工具,用于在一个或多个线程等待其他线程完成任务后再继续执行。

在创建 CountDownLatch 对象时,需要指定一个初始计数值(count)。
当一个线程完成了自己的任务后,可以调用 CountDownLatch 的 countDown() 方法,将 count 值减1。
其他线程可以通过调用 CountDownLatch 的 await() 方法来等待 count 值变为0。
当 count 值变为0 时,所有等待的线程会被唤醒,可以继续执行后续的操作。
简而言之,CountDownLatch 是通过一个计数器来实现线程间的等待和通知机制。线程在等待某些条件满足时会阻塞,直到计数器的值变为0,才能继续执行。

常见的应用场景是,将一个任务分解为多个子任务,并让多个线程并行执行这些子任务。主线程等待所有子线程完成任务后,再进行后续的处理。

需要注意的是,CountDownLatch 的计数值只能在初始化时设置一次,并且无法重置。一旦计数值变为0,就无法再次使用 CountDownLatch 进行等待。

注意事项

  1. 当count变为0时,即使调用了await()方法,线程也不会等待,CountDownLatch内部用CAS(乐观锁)的方式进行自旋;
  2. 如果count没有减为0,那么会一直自旋,导致线程死锁

代码演示

新建一个Worker类,将CountDownLatch 作为成员变量传入,同时在执行结束后,在finally中countDown,防止子线程异常结束后计数器没有减。

class Worker extends Thread {
    
    private CountDownLatch latch;

    /**
     * 等待时间
     */
    private long time;

    /**
     * 通过构造方法传入latch
     * @param latch
     */
    public Worker(CountDownLatch latch, long time) {
        this.latch = latch;
        this.time = time;
    }

    @Override
    public void run() {
        try {
            System.out.println("【子线程】" + Thread.currentThread().getName() + ", 开始执行......");
            Thread.sleep(time);
            System.out.println("【子线程】" + Thread.currentThread().getName() + ", 执行结束!");
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        } finally {
            //计数器减一 (防止线程崩掉,在finally中countDown)
            latch.countDown();
        }
    }
}

主线程中新建三个子线程,将latch传入,await()方法可以传入超时时间,如果在规定时间内没有返回,则计数器归零,主线程继续执行

	public static void main(String[] args) throws InterruptedException {
        //count初始量为3
        CountDownLatch latch = new CountDownLatch(3);

        System.out.println("【主线程】" + Thread.currentThread().getName() + ", 开始执行!");
        new Worker(latch, 2000).start();
        new Worker(latch, 1500).start();
        new Worker(latch, 5000).start();

//        boolean await = latch.await(4, TimeUnit.SECONDS);//等待4秒自动释放,防止死锁,返回false代表子线程超时,返回true代表正常结束
//        System.out.println("【主线程】" + Thread.currentThread().getName() + ", 全部结束!线程是否超时:" + await);
        latch.await(); //主线程等待子线程全部结束
        System.out.println("【主线程】" + Thread.currentThread().getName() + ", 全部结束!");
    }

结果

设置超时时间后的执行结果(未超时返回true, 超时返回false)

【主线程】main, 开始执行!
【子线程】Thread-0, 开始执行…
【子线程】Thread-1, 开始执行…
【子线程】Thread-2, 开始执行…
【子线程】Thread-1, 执行结束!
【子线程】Thread-0, 执行结束!
【主线程】main, 全部结束!线程是否超时:false //代表子线程超时了

未设置超时时间,会等待子线程全部结束

【主线程】main, 开始执行!
【子线程】Thread-0, 开始执行…
【子线程】Thread-1, 开始执行…
【子线程】Thread-2, 开始执行…
【子线程】Thread-1, 执行结束!
【子线程】Thread-0, 执行结束!
【子线程】Thread-2, 执行结束!
【主线程】main, 全部结束!

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值