Java并发编程(9)-CountDownLatch

本文深入探讨了Java并发编程中的闭锁概念,重点介绍了CountDownLatch类的使用方法及其实现线程控制的具体案例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >


更多关于Java并发编程的文章请点击这里:Java并发编程实践(0)-目录页


本文将介绍什么是闭锁,在java中的闭锁实现:CountDownLatch类及其常用方法等,最后给出了一个使用闭锁模拟线程并发的demo,用以简单地测试任务是否为线程安全。

一、什么是闭锁

闭锁(Latch)是在并发编程中常被提及的概念。闭锁是一种线程控制对象,它能让所有的线程在某个状态时终止工作并等待,直到闭锁“开门”时,所有的线程在这一刻会几乎同时执行工作,制造出一个并发的环境。

二、CountDownLatch类介绍

2.1、什么是CountDownLatch

CountDownLatch,顾名思义,可以理解为计数(count)、减少(down)、闭锁(Latch),即通过计数减少的方式来达到阻碍线程执行任务的一种闭锁,这个类位于java.util.concurent并发包下,是java中闭锁的最优实现。

2.2、构造方法

CountDownLatch(int count)
构造器中计数值(count)就是闭锁需要等待的线程数量,这个值只能被设置一次。

2.3、主要方法
  • void await():
    使当前线程在锁存器倒计数至零之前一直等待,除非线程被中断。

  • boolean await(long timeout, TimeUnit unit):
    使当前线程在锁存器倒计数至零之前一直等待,除非线程被中断或超出了指定的等待时间。

  • void countDown():
    递减锁存器的计数,如果计数到达零,则释放所有等待的线程。

  • long getCount():
    返回当前计数。

  • String toString():
    返回标识此锁存器及其状态的字符串。

三、闭锁的使用

1.一个计数器使用的例子:主线程等待其他所有线程执行完毕在执行
public class CountDownLatchDemo {
    private static int counter = 0;

    public static void main(String[] args) throws InterruptedException {
        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
                0, // corePoolSize
                Integer.MAX_VALUE, // maxPoolSize
                60L,
                TimeUnit.SECONDS,
                new SynchronousQueue<Runnable>()
        );
        int count = 5;
        CountDownLatch latch = new CountDownLatch(count);
        for (int i = 0; i < count; i++) {
            threadPoolExecutor.execute(() -> {
                try {
                    run();
                } catch (Exception e) {
                }finally {
                    latch.countDown();
                }
            });
        }
        latch.await();
        threadPoolExecutor.shutdown();
        System.out.println("线程执行完毕");
    }

    private static void run() throws InterruptedException {
        Thread.sleep(1000);
        System.out.println(Thread.currentThread());

    }
}

在这里插入图片描述

2.一个并发测试的例子:将所有的子线程全部阻塞在一个入口,等待主线程统一放行
public static void main(String[] args) throws InterruptedException {
        ExecutorService executorService = Executors.newCachedThreadPool();
        CountDownLatch countDownLatch = new CountDownLatch(1);
        int i = 0;
        for (i = 0; i <= 50; i++) {
            final int num = i;
            executorService.submit(()->{
                try {
                    countDownLatch.await();
                    System.out.println(String.format("线程%s打印%d", Thread.currentThread().getName(), num));
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            });
        }

        Thread.sleep(5000);

        System.out.println("FINISHED");
        countDownLatch.countDown();
        executorService.shutdown();
    }

可以看到执行结果中,counter的值出现了并发冲突:
在这里插入图片描述
这时可以使用原子类AtomicInteger,通过CAS去自增计数器的值(为什么不使用volatile呢?因为volatile虽然能保证多线程读写共享变量的可见性,但修改一个变量并写入毕竟是分为读-改-写这三个过程的,所以仍需要使用CAS来保障)。

private static AtomicInteger counter = new AtomicInteger();
    private static void run() throws InterruptedException {
        // 使用原子类 CAS更新值
        System.out.println(Thread.currentThread() + ":" + counter.incrementAndGet());
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

BoringRong

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

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

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

打赏作者

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

抵扣说明:

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

余额充值