Java多线程同步工具类CountDownLatch详解

2022-11-13 11:11:46 多线程 同步 详解

简介

CountDownLatch是一个多线程同步工具类,在多线程环境中它允许多个线程处于等待状态,直到前面的线程执行结束。从类名上看CountDown既是数量递减的意思,我们可以把它理解为计数器。

核心方法

  • countDown():计数器递减方法。
  • await():使调用此方法的线程进入等待状态,直到计数器计数为0时主线程才会被唤醒。
  • await(long, TimeUnit):在await()方法的基础上增加了超时策略,若等待超时仍未有结果则会直接唤醒主线程运行。

CountDownLatch如何使用

在这里我们用一段简单的代码进行演示:

@Slf4j
public class CountDownLatchDemo {
    public static void main(String[] args) throws InterruptedException {
        CountDownLatch countDownLatch = new CountDownLatch(3);
        new Thread(() -> {
            log.info("hello this is thread one");
            try {
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            countDownLatch.countDown();
        }).start();
        new Thread(() -> {
            log.info("hello this is thread two");
            countDownLatch.countDown();
        }).start();
        new Thread(() -> {
            log.info("hello this is thread three");
            countDownLatch.countDown();
        }).start();
        countDownLatch.await();
        log.info("say Good bye!");
    }
}

由上面的代码可见,我们创建了一个CountDownLatch计数器为3和三个线程同步运行。在main主线程中调用了countDownLatch.await()方法使主线程进入阻塞。其中三个线程任务执行完毕后都会调用countDownLatch.countDown()方法对计数器进行递减,当三个线程任务都执行完毕后计数器计数值为0时主线程被唤醒。

注:在创建CountDownLatch实例时必须定义计数器值,一般相对较合理的用法是该值的定义需要经过合理的计算使计数值与需要并行的线程数相等,在每个线程执行完成后做计数递减,最终唤醒主线程继续执行。

  • CountDownLatch计数值设置大于线程数,那么最终所有线程都执行完了,而计数为递减到0那么主线程将会一直处于等待状态。
  • CountDownLatch计数值设置小于并发线程数,那么可能在部分线程未执行完毕时,计数就已经递减到0,则主线程会被提前唤醒。

CountDownLatch运行流程

如下图,主线程阻塞与唤醒的核心就是计数器,只有当所有线程执行完成计数逐个递减最终才会唤起await()阻塞中的主线程。

注:await()可以阻塞一个线程,也可以阻塞多个线程,如果是阻塞多个线程,那么在计数为0时将会唤醒所有被阻塞的线程。

运用场景

在简单了解完CountDownLatch的作用后,相信各位最终目的还是想了解如何去使用,在哪些场景下使用更加合适,接下来我就拿一个对账业务的场景详细分析一下。

相信现在很多平台都会对接银联、微信、支付宝等支付渠道做交易,那么在这样的场景下对账是不可避免的。对账通常都会在每日的凌晨去处理,一方面是凌晨时间点多数平台访问量都会较小,服务器压力也比较轻松,而且此时出账也比较合理,所以在这个时间点做对账也是一个大数据量计算的操作。

上面讲这么多好像都没说到重点,在处理对账之前首先我们肯定是需要通过各个支付渠道获取对账单文件,那么该如何操作呢?

  • 对账文件下载(第一阶段):在这种情况下可以设计三个任务并发去获取对账文件,使用CountDownLatch阻塞主线程,等待三个任务都获取到文件的时候做计数递减,最终唤醒主线将标记本阶段处理完成,并发起进入下一阶段的通知。
  • 对账文件解析(第二阶段):在上个阶段已下载完成的文件文件中,此阶段要做的就是解析文件。由于三个渠道都是不同的厂家那么文件的内容格式肯定都是不一样的,这时候我们又可以使用CountDownLatch启动三个线程分别去解析各自的对账文件,最终将文件内容转换为业务所需的数据统一格式入库,在三个任务都入库完成后主线程又被唤醒标记完成后,通知下一阶段开始进入工作。
  • 对账结算(第三阶段):在上一阶段的数据入库完成后,此阶段要做的就是比对每一笔交易是否准确,一般都是按单号与交易渠道比对交易的金额是否一致,如果金额一致则该笔交易结算成功,否则将交易判定为异常交易,并入库处理。由上面的流程分析我们就可以设计相对合理的CountDownLatch计数,结合Semaphore信号量控制并发量同时对对比交易单做并发处理,最终带所有交易单处理完成后唤醒主线程标记对账完成,并通知下一阶段进行出账。
  • 出账(第四阶段):通常平台在对账完成后会进行出账,也就是按照平台的业务规则出具相关的账单方便财务人员进行统计。

总结

多线程并发的情况下需要做好同步处理,结合CountDownLatch充分的运用到业务场景当中还是挺有必要的,凡是需要在多个任务执行完成后再去做另一件事的情况都可以考虑使用CountDownLatch,合理使用但请不要滥用,特别上面也提到过计数值需要确定,否则可能导致多任务无法做到同步甚至造成主线程无限等待。

到此这篇关于Java多线程同步工具类CountDownLatch详解的文章就介绍到这了,更多相关Java CountDownLatch内容请搜索以前的文章或继续浏览下面的相关文章希望大家以后多多支持!

相关文章