class="MsoNormal">CountDownLatch,它可以阻塞一个或多个线程,以等待另一组事件的发生后,继续执行被阻塞的一个或多个线程。CountDownLatch的两个核心方法:调用await方法阻塞一个或多个线程;调用countDown方法,执行一组事件,每调用一次对“资源”数减1,当剩余“资源”数为0时,被阻塞的一个或多个线程同时被唤醒。这其实就是AQS的共享方式实现,在分析CountDownLatch实现原理之前,先来简单看看CountDownLatch的使用。
?
这有点类似游戏里组队玩游戏,假设游戏需要10个人,每个人加入游戏时都会被阻塞在开始界面,只有等人满后才能点击开始游戏。模拟代码如下:
Java代码??
- ???
- ?
- ?
- ??
- public?class?CountDownLatchTest?{??
- ???
- ????public?static?void?main(String[]?args)?throws?InterruptedException?{??
- ????????ExecutorService?executorService?=?Executors.newFixedThreadPool(10);??
- ????????CountDownLatch?playersCounter?=?new?CountDownLatch(10);??
- ????????for?(int?i=0;i<10;i++){??
- ????????????executorService.submit(new?Thread(new?Player(playersCounter,i+"")));??
- ????????}??
- ???
- ????????System.out.println("等待玩家加入游戏");??
- ??????????
- ????????playersCounter.await();??
- ????????System.out.println("游戏开始");??
- ???
- ????????executorService.shutdown();??
- ????????System.out.println("游戏结束");??
- ????}??
- }??
- ???
- ??
- class?Player?implements?Runnable{??
- ????private?CountDownLatch?playersCounter;??
- ????private?String?name;??
- ???
- ????public?Player(CountDownLatch?playersCounter,String?name)?{??
- ????????this.playersCounter?=?playersCounter;??
- ????????this.name?=?name;??
- ????}??
- ???
- ????@Override??
- ????public?void?run()?{??
- ????????System.out.println("玩家:"+name+"加入游戏");??
- ????????playersCounter.countDown();??
- ????}??
- }??
- ???
?
执行上述main方法,可以发现只有等10个玩家都准备就绪后,游戏才能开始。可以发现使用CountDownLatch很简单,但我们不能仅仅停留在如何使用上,还应该更进一步了解其内部原理,以便再必要的时候创建自己的同步器。
?
CountDownLatch实现原理解析
?
在已经全面理解了AQS的前提下(见前一篇文章),再来看CountDownLatch内部实现,你会觉得非常简单。其内部核心实现就是,定义了一个实现了AQS的内部类Sync,AQS的公共资源状态字段state的值,就是初始化CountDownLatch(int count)的count值;调用CountDownLatch的await方法,会判断state的值是否变为0,如果不为0就阻塞该线程;调用CountDownLatch的countDown方法,没调用一次会对state减1,直到为0时,唤醒所有被阻塞的线程。
?
首先来看下内部类Sync的实现,AQS预留给子类实现的方法分为两类:排它和共享,排它 获取和释放方法分别为tryAcquir和tryRelease;共享 获取和释放方法分别为tryAcquireShared和tryReleaseShared。如果把CountDownLatch理解为锁的话,它属于共享锁的一种,因为所有阻塞的线程共享同一个开关,所以在CountDownLatch中Sync对AQS的实现,应该是共享实现,也就是说实现了tryAcquireShared和tryReleaseShared方法。我们来看下源码:
Java代码??
- 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)?{??
- ??????????
- ????????for?(;;)?{??
- ????????????int?c?=?getState();??
- ????????????if?(c?==?0)??
- ????????????????return?false;??
- ????????????????????????
- ????????????int?nextc?=?c-1;??
- ????????????if?(compareAndSetState(c,?nextc))??
- ????????????????return?nextc?==?0;??
- ????????}??
- ????}??
- }??
主要的方法就是构造方法Sync(int count)、获取资源方法tryAcquireShared(int acquires)、释放资源方法tryReleaseShared(int releases)。这三个方法分别会被CountDownLatch调用:
?
CountDownLatch构造方法
CountDownLatch的构造方法会调用Sync的构造方法Sync(int count),为AQS的state赋值;
Java代码??
- public?CountDownLatch(int?count)?{??
- ????????if?(count?<?0)?throw?new?IllegalArgumentException("count?<?0");??
- ????????this.sync?=?new?Sync(count);??
- ????}??
- ???
?
CountDownLatch的await方法
CountDownLatch的await方法会调用Sync的tryAcquireShared方法,尝试获取锁,如果获取不到就阻塞;
Java代码??
- public?void?await()?throws?InterruptedException?{??
- ??????????
- ????????sync.acquireSharedInterruptibly(1);??
- ????}??
- ???
?
CountDownLatch的countDown方法
CountDownLatch的countDown方法会调用Sync的tryReleaseShared方法释放资源,当state值变为0时,唤醒所有被阻塞的线程。
Java代码??
- public?void?countDown()?{??
- ????????sync.releaseShared(1);??
- }??
- ???
?
另外CountDownLatch的await方法还有延迟版本:
Java代码??
- public?boolean?await(long?timeout,?TimeUnit?unit)??
- ????????throws?InterruptedException?{??
- ????????return?sync.tryAcquireSharedNanos(1,?unit.toNanos(timeout));??
- }??
?
对应CountDownLatch的两个await方法,也许你已经注意到了 它们都会抛出InterruptedException异常,说明CountDownLatch实现的闭锁是可中断锁,调用线程的interrupt方法,可以中断阻塞的线程。
?
总结
?
?
简单的总结CountDownLatch闭锁就是:它实现了一个“共享锁”,可以阻塞一个或多个线程,以等待另一组事件的发生后,继续执行被阻塞的一个或多个线程。其核心实现就是基于AQS,另外CountDownLatch闭锁是“可中断锁”。
?
http://moon-walker.iteye.com/blog/2406502