Java CountDownLatch的使用方法

摘要:介绍Java中CountDownLatch的使用方法,重点包括计数器的值、countDown和await。计数器的值表示任务线程的个数,每次countDown都会使计数减一,减到0的时候调用await方法的线程就不再被阻塞。

综述

  CountDownLatch一般被称作"计数器",计数器的初始值是线程的数量,是一个同步工具类,用来协调多个线程之间的同步。其中,count down是倒数的意思,latch则是门闩的含义,整体含义可以理解为倒数的门栓;有一点“三二一,芝麻开门”的感觉。

  CountDownLatch在初始化时,需要指定一个正整数作为计数器起始值,每当一个线程执行完毕后,计数器就被countDown方法减1,当计数器为0时,表示所有线程都执行完毕,然后在闭锁上等待的线程就被唤醒,恢复工作。从线程执行状态的角度分析,就是允许一个或多个线程等待,直到其它线程执行完成才继续往下走。

  CountDownLatch是在java 1.5 被引入的,与它一起被引入的工具类还包括CyclicBarrier、Semaphore、ConcurrentHashMap和BlockingQueue等,它们都位于java.util.cucurrent包下。

  CountDownLatch的计数器无法被重置,当计数器被减到0时,调用await方法会直接返回。这一点有别于Semaphore,Semaphore是可以通过release操作恢复信号量的。

CountDownLatch类

  CountDownLatch 类提供了三个核心构造器:

//参数count为计数值,即线程个数
public CountDownLatch(int count) {  };  
    /**
     * Causes the current thread to wait until the latch has counted down to
     * zero, unless the thread is {@linkplain Thread#interrupt interrupted}.
     *
     * <p>If the current count is zero then this method returns immediately.
     *
     * 调用await()方法的线程会被挂起,直到count值为0才继续执行
     */
public void await() throws InterruptedException { };   
//和await()类似,只不过等待一定的时间后,如果计数器的值还没倒数至0,就会被唤醒继续执行
public boolean await(long timeout, TimeUnit unit) throws InterruptedException { };  
    /**
     * Decrements the count of the latch, releasing all waiting threads if
     * the count reaches zero.
     * 每次减少一个门闩数,如果等于零,则释放等待线程
     * <p>If the current count is greater than zero then it is decremented.
     * If the new count is zero then all waiting threads are re-enabled for
     * thread scheduling purposes.
     *
     * <p>If the current count equals zero then nothing happens.
     */
public void countDown() { }; 

  如果想使线程处于等待状态,则调用await()方法,此线程一般是主线程。温馨提示,await()方法并没有规定只能由一个线程执行,如果多个线程同时执行await()方法,那么这几个线程都将处于等待状态,并且以共享模式享有同一把锁。另外,当同一个线程多次调用countDown()方法时,每次都会使计数器减一;

  简单总结一下,CountDownLatch有三个重点:计数器的值、await、countDown。计数器的值表示任务线程的个数,每次countDown都会使计数减一,减到0的时候await方法所在的线程就不再被阻塞。

使用场景

  CountDownLatch非常适合于对任务进行拆分,使其并行执行,实现最大的并行性。某一线程A在开始运行前一直被阻塞,需等待n个其它线程执行完毕,则需将CountDownLatch的计数器初始化为 new CountDownLatch(n),每当一个任务线程执行完毕,则调用countDown()函数使得计数器减一;当计数器变为0时,在CountDownLatch上await()的线程A就会被唤醒。

  例1,会计汇总一年的财务报表时,可以把任务按照季度拆分为四份,那么就可以将这个大任务拆分为4个子任务,分别交由4个财务(线程)执行,执行完成之后再由主财务(主线程)进行财务汇总。此时,总的执行时间将决定于执行最慢的任务,通常来看,还是可以大大减少总的执行时间。

  假2,有10个线程需要加载一些资源,而另外一个线程A必须在所有资源被加载完成后才能继续执行,那么我们可以new一个CountDownLatch(10)的闭锁,每个加载资源的线程执行完后都调用countDown()函数,而线程A则在需要的资源准备齐全之前因调用await函数而被阻塞,当计数器的值为0时,线程A就会被唤醒继续执行。

知行合一

  案例请戳《Java 使用线程池和CountDownLatch分批插入或者更新数据》。

结束语

  JUC CountDownLatch是一个同步工具类,用来协调多个线程之间的同步。这个工具通常用来控制线程等待,它可以让某一个线程等待直到倒计时结束,再开始执行。原理:它通过一个计数器来实现的,计数器的初始化值为线程的数量。每当一个线程完成了自己的任务后,计数器的值就减一。当计数器递减至零时,表示所有的线程都已完成任务,然后在闭锁上等待的线程就被唤醒,恢复执行任务。

  老铁们, 因个人能力有限,难免有瑕疵,如果发现bug或者有更好的建议,那么请在文章下方留言!

posted @ 2022-04-30 21:06  楼兰胡杨  阅读(121)  评论(0编辑  收藏  举报