Java闭锁—CountDownLatch_JAVA_编程开发_程序员俱乐部

中国优秀的程序员网站程序员频道CXYCLUB技术地图
热搜:
更多>>
 
您所在的位置: 程序员俱乐部 > 编程开发 > JAVA > Java闭锁—CountDownLatch

Java闭锁—CountDownLatch

 2018/1/4 15:36:13  aoyouzi  程序员俱乐部  我要评论(0)
  • 摘要:CountDownLatch,它可以阻塞一个或多个线程,以等待另一组事件的发生后,继续执行被阻塞的一个或多个线程。CountDownLatch的两个核心方法:调用await方法阻塞一个或多个线程;调用countDown方法,执行一组事件,每调用一次对“资源”数减1,当剩余“资源”数为0时,被阻塞的一个或多个线程同时被唤醒。这其实就是AQS的共享方式实现,在分析CountDownLatch实现原理之前,先来简单看看CountDownLatch的使用。这有点类似游戏里组队玩游戏
  • 标签:Java

class="MsoNormal">CountDownLatch,它可以阻塞一个或多个线程,以等待另一组事件的发生后,继续执行被阻塞的一个或多个线程。CountDownLatch的两个核心方法:调用await方法阻塞一个或多个线程;调用countDown方法,执行一组事件,每调用一次对“资源”数减1,当剩余“资源”数为0时,被阻塞的一个或多个线程同时被唤醒。这其实就是AQS的共享方式实现,在分析CountDownLatch实现原理之前,先来简单看看CountDownLatch的使用。

?

这有点类似游戏里组队玩游戏,假设游戏需要10个人,每个人加入游戏时都会被阻塞在开始界面,只有等人满后才能点击开始游戏。模拟代码如下:

Java代码??收藏代码
  1. ???
  2. /**?
  3. ?*?Created?by?gantianxing?on?2018/1/3.?
  4. ?*/??
  5. public?class?CountDownLatchTest?{??
  6. ???
  7. ????public?static?void?main(String[]?args)?throws?InterruptedException?{??
  8. ????????ExecutorService?executorService?=?Executors.newFixedThreadPool(10);??
  9. ????????CountDownLatch?playersCounter?=?new?CountDownLatch(10);??
  10. ????????for?(int?i=0;i<10;i++){??
  11. ????????????executorService.submit(new?Thread(new?Player(playersCounter,i+"")));??
  12. ????????}??
  13. ???
  14. ????????System.out.println("等待玩家加入游戏");??
  15. ????????//这里只模拟了一个线程阻塞,可以多线程调用await()阻塞??
  16. ????????playersCounter.await();??
  17. ????????System.out.println("游戏开始");??
  18. ???
  19. ????????executorService.shutdown();//关闭线程池??
  20. ????????System.out.println("游戏结束");??
  21. ????}??
  22. }??
  23. ???
  24. //多线程操作线程不安全容器?ThreadSafe.datas??
  25. class?Player?implements?Runnable{??
  26. ????private?CountDownLatch?playersCounter;??
  27. ????private?String?name;??
  28. ???
  29. ????public?Player(CountDownLatch?playersCounter,String?name)?{??
  30. ????????this.playersCounter?=?playersCounter;??
  31. ????????this.name?=?name;??
  32. ????}??
  33. ???
  34. ????@Override??
  35. ????public?void?run()?{??
  36. ????????System.out.println("玩家:"+name+"加入游戏");??
  37. ????????playersCounter.countDown();//对剩余的游戏坑位减1??
  38. ????}??
  39. }??
  40. ???

?

执行上述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代码??收藏代码
  1. private?static?final?class?Sync?extends?AbstractQueuedSynchronizer?{??
  2. ????private?static?final?long?serialVersionUID?=?4982264981922014374L;??
  3. ???
  4. ????Sync(int?count)?{??
  5. ????????setState(count);//设置AQS的队列状态state字段??
  6. ????}??
  7. ???
  8. ????int?getCount()?{??
  9. ????????return?getState();??
  10. ????}??
  11. ???
  12. ????protected?int?tryAcquireShared(int?acquires)?{??
  13. ??????????????//判断AQS的队列状态state值是否变为0,如果不为0,阻塞该线程??
  14. ????????return?(getState()?==?0)???1?:?-1;??
  15. ????}??
  16. ???
  17. ????protected?boolean?tryReleaseShared(int?releases)?{??
  18. ????????//?Decrement?count;?signal?when?transition?to?zero??
  19. ????????for?(;;)?{??
  20. ????????????int?c?=?getState();??
  21. ????????????if?(c?==?0)??
  22. ????????????????return?false;??
  23. ??????????????????????//尝试对state字段减1??
  24. ????????????int?nextc?=?c-1;??
  25. ????????????if?(compareAndSetState(c,?nextc))//CAS原子修改state值??
  26. ????????????????return?nextc?==?0;??
  27. ????????}??
  28. ????}??
  29. }??

主要的方法就是构造方法Sync(int count)、获取资源方法tryAcquireShared(int acquires)、释放资源方法tryReleaseShared(int releases)。这三个方法分别会被CountDownLatch调用:

?

CountDownLatch构造方法

CountDownLatch的构造方法会调用Sync的构造方法Sync(int count),为AQS的state赋值;

Java代码??收藏代码
  1. public?CountDownLatch(int?count)?{??
  2. ????????if?(count?<?0)?throw?new?IllegalArgumentException("count?<?0");??
  3. ????????this.sync?=?new?Sync(count);//?调用Sync的构造方法Sync(int?count)??
  4. ????}??
  5. ???

?

CountDownLatch的await方法

CountDownLatch的await方法会调用Sync的tryAcquireShared方法,尝试获取锁,如果获取不到就阻塞;

Java代码??收藏代码
  1. public?void?await()?throws?InterruptedException?{??
  2. ????????//AQS的acquireSharedInterruptibly方法内部会调用tryAcquireShared方法??
  3. ????????sync.acquireSharedInterruptibly(1);??
  4. ????}??
  5. ???

?

CountDownLatch的countDown方法

CountDownLatch的countDown方法会调用Sync的tryReleaseShared方法释放资源,当state值变为0时,唤醒所有被阻塞的线程。

Java代码??收藏代码
  1. public?void?countDown()?{??
  2. ????????sync.releaseShared(1);//没调用一次,就会对AQS的state字段减1??
  3. }??
  4. ???

?

另外CountDownLatch的await方法还有延迟版本

Java代码??收藏代码
  1. public?boolean?await(long?timeout,?TimeUnit?unit)??
  2. ????????throws?InterruptedException?{??
  3. ????????return?sync.tryAcquireSharedNanos(1,?unit.toNanos(timeout));??
  4. }??

?

对应CountDownLatch的两个await方法,也许你已经注意到了 它们都会抛出InterruptedException异常,说明CountDownLatch实现的闭锁是可中断锁,调用线程的interrupt方法,可以中断阻塞的线程。

?

总结

?

?

简单的总结CountDownLatch闭锁就是:它实现了一个“共享锁”,可以阻塞一个或多个线程,以等待另一组事件的发生后,继续执行被阻塞的一个或多个线程。其核心实现就是基于AQS,另外CountDownLatch闭锁是“可中断锁”。

?

http://moon-walker.iteye.com/blog/2406502

发表评论
用户名: 匿名