并发是个什么鬼之同步工具类CountDownLatch
- 2019 年 12 月 5 日
- 筆記
CountDownLatch 又是大神 Doug Lea的又一神作,正如每个Java文档所描述的那样,CountDownLatch是一个同步工具类,它允许一个或多个线程一直等待,直到其他线程的操作执行完后再执行。
CountDownLatch是通过一个计数器来实现的,计数器的初始值为线程的数量。每当一个线程完成了自己的任务后,计数器的值就会减1。当计数器值到达0时,它表示所有的线程已经完成了任务,然后在闭锁上等待的线程就可以恢复执行任务。
源码分析
源码位于rt.java中的java.util.concurrent包中。
1、CountDownLatch:A synchronization aid that allows one or more threads to wait until a set of operations being performed in other threads completes.
中文解释:也就是说主线程在等待所有其它的子线程完成后再往下执行
2、构造函数:CountDownLatch(int count)//初始化count数目的同步计数器,只有当同步计数器为0,主线程才会向下执行
主要方法:void await()//当前线程等待计数器为0 boolean await(long timeout, TimeUnit unit)//与上面的方法不同,它加了一个时间限制。 void countDown()//计数器减1 long getCount()//获取计数器的值
3.它的内部有一个辅助的内部类:sync. 它的实现如下:
/** * Synchronization control For CountDownLatch. * Uses AQS state to represent count. */ private static final class Sync extends AbstractQueuedSynchronizer { private static final long serialVersionUID = 4982264981922014374L; Sync(int count) { setState(count); } int getCount() { return getState(); } protected int tryAcquireShared(int acquires) { return (getState() == 0) ? 1 : -1; } protected boolean tryReleaseShared(int releases) { // Decrement count; signal when transition to zero for (;;) { int c = getState(); if (c == 0) return false; int nextc = c-1; if (compareAndSetState(c, nextc)) return nextc == 0; } } }
4.await()方法的实现
public void await() throws InterruptedException { sync.acquireSharedInterruptibly(1); } public final void acquireSharedInterruptibly(int arg) throws InterruptedException { if (Thread.interrupted()) throw new InterruptedException(); //调用3中的tryAcquireShared()方法 if (tryAcquireShared(arg) < 0) doAcquireSharedInterruptibly(arg);//加入到等待队列中 }
5.countDown()方法的实现
public void countDown() { sync.releaseShared(1); } public final boolean releaseShared(int arg) { //调用3中的tryReleaseShared()方法 if (tryReleaseShared(arg)) { //解锁 doReleaseShared(); return true; } return false; }
应用场景
比如主任务是一个比较复杂的运算,为了节约时间,我们可以拆分成多个子任务,多线程同时执行,最终统一汇总任务。
其实生活也有类似的场景,比如马拉松赛跑,我们不可能按顺序依次跑步,这样得跑到猴年马月。一般来说都是大家听到发号命令一起跑,最终比赛结束,统一汇总成绩。
代码案例
下面看一个例子大家就清楚CountDownLatch的用法了:
/** * CountDownLatch,一个同步辅助类,在完成一组正在其他线程中执行的操作之前,它允许一个或多个线程一直等待 * 构造方法参数指定了计数的次数 * countDown方法,当前线程结束执行后调用,计数减一 * awaint方法,调用此方法会一直阻塞当前线程,直到计时器的值为0 * 创建者 科帮网 * 创建时间 2017年8月15日 * */ public class CountDownLatchDemo { final static SimpleDateFormat sdf = new SimpleDateFormat( "yyyy-MM-dd HH:mm:ss"); final static String startTime = sdf.format(new Date()); public static void main(String[] args) throws InterruptedException { CountDownLatch latch = new CountDownLatch(2);// 两个赛跑者 Runer runer1 = new Runer("刘翔", 5000, latch); Runer runer2 = new Runer("罗伯斯", 8000, latch); runer1.start();//刘翔 开始跑步 runer2.start();//罗伯斯 开始跑步 latch.await();// 等待所有人赛跑结束 System.out.println("all runer done at " + sdf.format(new Date())); } static class Runer extends Thread { String runerName; int runTime; CountDownLatch latch; public Runer(String runerName, int runTime, CountDownLatch latch) { this.runerName = runerName; this.runTime = runTime; this.latch = latch; } public void run() { System.out.println("Runer " + runerName + " do run begin at " + startTime); doRun();//跑步 System.out.println("Runer " + runerName + " do run complete at " + sdf.format(new Date())); latch.countDown();// 终点结束,计数器减一 } private void doRun() { try { Thread.sleep(runTime); } catch (InterruptedException e) { e.printStackTrace(); } } } }