简介
CountDownLatch 定义了一个计数器和一个阻塞队列, 当计数器的值递减为0之前,阻塞队列里面的线程处于挂起状态,当计数器递减到0时会唤醒阻塞队列中的所有线程,这里的计数器是一个标志,可以表示一个任务一个线程,也可以表示一个倒计时器,CountDownLatch可以解决那些一个或者多个线程在执行之前必须依赖于某些必要的前提业务先执行的场景。
常用方法
CountDownLatch(int count); //构造方法,创建一个值为count 的计数器。
await();//阻塞当前线程,将当前线程加入阻塞队列。
await(long timeout, TimeUnit unit);//在timeout的时间之内阻塞当前线程,时间一过则当前线程可以执行,
countDown();//对计数器进行递减1操作,当计数器递减至0时,当前线程会去唤醒阻塞队列里的所有线程。
应用
需求场景
某系统中,需要支持批量导入数据,在执行批量导入后,每一批数据会生成一个导入单号,导入单号会立刻返回给用户,每一条数据在后台执行异步导入,导入完成后需要统计导入总数、导入成功/失败数及导入完成时间,用户可以在系统中查看导入单对应的导入状态(导入中/全部成功/全部失败/部分成功)。
解决方案
实现方案:
通过线程池异步执行每条数据的导入,并借助CountDownLatch类待所有数据导入完成后进行导入数据的统计,为了不阻塞主线程将导入单号返回给用户,则新开一个线程去等待数据全部导入完成后进行导入数据的统计。为了避免数据导入过程中宕机导致导入单未完成全部数据导入,需要在服务启动时,对扫描是否存在未完成的导入单,如果存在需要对未完成的导入单继续执行导入操作。在服务启动时的扫描程序可能会与导入线程发生线程安全问题,例如,服务启动扫描未完成的导入单时,用户新导入了一批数据,恰巧被服务启动的扫描线程获取到,则会导致多个线程对同一张单执行导入操作,从而引发线程安全问题,因此,对每条数据执行导入操作时需要加锁避免线程安全问题。
实现流程图: