您是否曾经使用过java.util.concurrent.CountDownLatch ?

这是在两个或多个线程之间实现同步的非常方便的类,在该类中,一个或多个线程可以等待,直到在其他线程中执行的一组操作完成为止(请参阅javadoc和此文章 )。 CountDownLatch在适当的情况下可以节省您的时间,您必须了解此类。

与往常一样,如果代码不好,线程同步会引发死锁。 由于并发用例可能非常复杂,因此开发人员必须非常小心。 在这里我不会描述复杂的并发问题,但是如果您不小心使用CountDownLatch ,可能会遇到一个简单的问题。

假设您有2个线程(线程1和线程2)共享一个java.util.concurrent.ArrayBlockingQueue,并且您想使用CountDownLatch对其进行同步。 检查以下简单示例:

package gr.qiozas.simple.threads.countdownlatch;import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.CountDownLatch;public class DeadlockCaseCDL {public static void main(String[] args) throws InterruptedException {CountDownLatch c = new CountDownLatch(1);ArrayBlockingQueue b = new ArrayBlockingQueue(1);new Thread(new T1(c, b)).start();new Thread(new T2(c, b)).start();}private static class T1 implements Runnable {private CountDownLatch c;private ArrayBlockingQueue b;private T1(CountDownLatch c, ArrayBlockingQueue b) {this.c = c; this.b = b;}public void run() {try {b.put(234);b.put(654);doWork(T1.class);c.countDown();doWork(T1.class);} catch (InterruptedException ex) {}}}private static class T2 implements Runnable {private CountDownLatch c;private ArrayBlockingQueue b;private T2(CountDownLatch c, ArrayBlockingQueue b) {this.c = c; this.b = b;}public void run() {try {doWork(T2.class);c.await();doWork(T2.class);System.out.println("I just dequeue "+b.take());System.out.println("I just dequeue "+b.take());} catch (InterruptedException ex) {}}}private static void doWork(Class classz) {System.out.println(classz.getName()+" do the work");}
}

您在上面看到的是,主线程创建了一个计数为1的CountDownLatch。 和一个容量为“ 1”的ArrayBlockingQueue 然后生成“ 2个线程”。 ArrayBlockingQueue将用于实际的“工作”(入队和出队),而CountDownLatch将用于同步线程(入队必须在出队前完成)。

线程1使2条消息入队,线程2希望使它们出队,但仅在线程1使两条消息入队之后。 此同步由CountDownLatch保证。 您认为此代码可以吗? 不,这不是造成死锁的原因!

如果仔细看第10行,您将看到我初始化ArrayBlockingQueue的容量等于“ 1”。 但是线程1要排队2条消息,然后释放(CountDownLatch)的锁,以便随后被线程2占用。 容量“ 1? 队列阻塞线程1,直到另一个线程将一个消息从队列中出队,然后再次尝试使第二条消息入队。 不幸的是,只有线程2使消息从队列中出队,但是由于线程1拥有CountDownLatch锁,因此线程2无法使任何消息出队,因此被阻塞。 因此,由于两个线程都被阻塞(等待获取不同的锁),我们确实有一个死锁 。 线程1等待ArrayBlockingQueue锁定,而线程2等待CountDownLatch锁定(您也可以在下面的相关线程转储中看到它)。

如果我们增加队列的容量,那么此代码将毫无问题地运行,但这不是本文的重点。 您需要了解的是,必须谨慎使用CountDownLatch,以避免此类危险情况。 您必须了解类的功能情况,向团队的其他开发人员详细说明该功能,编写有用的Javadoc,并始终编写在极端情况下(不仅对于开心路径而言)都可靠的代码。

您可能会帮助您的另一点是,现代JVM无法检测到此死锁。 尝试一下。

如您所知,现代JVM(Hotspot和JRockit)都能够检测到简单的死锁,并在线程转储中报告它们。 请参阅从Hotspot JVM检测到的简单死锁示例:

Found one Java-level deadlock:
=============================
"Thread-6":
waiting to lock monitor 0x00a891ec (object 0x06c616e0, a java.lang.String),
which is held by "Thread-9"
"Thread-9":
waiting to lock monitor 0x00a8950c (object 0x06c61708, a java.lang.String),
which is held by "Thread-6"

您可以尝试DeadlockCaseCDL并获得线程转储(在GNU / Linux上仅运行“ kill -3 ‹jvm_pid›”)。 您将看到线程转储看起来很正常,JVM没有检测到死锁,但是您处于死锁状态!!! 因此,请注意,JVM无法检测到这种死锁。

从我的本地执行中检查以下线程转储示例:

Full thread dump Java HotSpot(TM) Server VM (17.1-b03 mixed mode):"DestroyJavaVM" prio=10 tid=0x0946e800 nid=0x5382 waiting on condition [0x00000000]java.lang.Thread.State: RUNNABLE"Thread-1" prio=10 tid=0x094b1400 nid=0x5393 waiting on condition [0x7c79a000]java.lang.Thread.State: WAITING (parking)at sun.misc.Unsafe.park(Native Method)- parking to wait for   (a java.util.concurrent.CountDownLatch$Sync)at java.util.concurrent.locks.LockSupport.park(LockSupport.java:158)at java.util.concurrent.locks.AbstractQueuedSynchronizer.parkAndCheckInterrupt(AbstractQueuedSynchronizer.java:811)at java.util.concurrent.locks.AbstractQueuedSynchronizer.doAcquireSharedInterruptibly(AbstractQueuedSynchronizer.java:969)at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquireSharedInterruptibly(AbstractQueuedSynchronizer.java:1281)at java.util.concurrent.CountDownLatch.await(CountDownLatch.java:207)at gr.qiozas.simple.threads.countdownlatch.DeadlockCaseCDL$T2.run(DeadlockCaseCDL.java:50)at java.lang.Thread.run(Thread.java:662)"Thread-0" prio=10 tid=0x094afc00 nid=0x5392 waiting on condition [0x7c7eb000]java.lang.Thread.State: WAITING (parking)at sun.misc.Unsafe.park(Native Method)- parking to wait for   (a java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject)at java.util.concurrent.locks.LockSupport.park(LockSupport.java:158)at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await(AbstractQueuedSynchronizer.java:1987)at java.util.concurrent.ArrayBlockingQueue.put(ArrayBlockingQueue.java:252)at gr.qiozas.simple.threads.countdownlatch.DeadlockCaseCDL$T1.run(DeadlockCaseCDL.java:29)at java.lang.Thread.run(Thread.java:662)"Low Memory Detector" daemon prio=10 tid=0x0947f800 nid=0x5390 runnable [0x00000000]java.lang.Thread.State: RUNNABLE"CompilerThread1" daemon prio=10 tid=0x7cfa9000 nid=0x538f waiting on condition [0x00000000]java.lang.Thread.State: RUNNABLE"CompilerThread0" daemon prio=10 tid=0x7cfa7000 nid=0x538e waiting on condition [0x00000000]java.lang.Thread.State: RUNNABLE"Signal Dispatcher" daemon prio=10 tid=0x7cfa5800 nid=0x538d waiting on condition [0x00000000]java.lang.Thread.State: RUNNABLE"Finalizer" daemon prio=10 tid=0x7cf96000 nid=0x538c in Object.wait() [0x7cd15000]java.lang.Thread.State: WAITING (on object monitor)at java.lang.Object.wait(Native Method)- waiting on  (a java.lang.ref.ReferenceQueue$Lock)at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:118)- locked  (a java.lang.ref.ReferenceQueue$Lock)at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:134)at java.lang.ref.Finalizer$FinalizerThread.run(Finalizer.java:159)"Reference Handler" daemon prio=10 tid=0x7cf94800 nid=0x538b in Object.wait() [0x7cd66000]java.lang.Thread.State: WAITING (on object monitor)at java.lang.Object.wait(Native Method)- waiting on  (a java.lang.ref.Reference$Lock)at java.lang.Object.wait(Object.java:485)at java.lang.ref.Reference$ReferenceHandler.run(Reference.java:116)- locked  (a java.lang.ref.Reference$Lock)"VM Thread" prio=10 tid=0x7cf92000 nid=0x538a runnable"GC task thread#0 (ParallelGC)" prio=10 tid=0x09475c00 nid=0x5383 runnable"GC task thread#1 (ParallelGC)" prio=10 tid=0x09477000 nid=0x5384 runnable"GC task thread#2 (ParallelGC)" prio=10 tid=0x09478800 nid=0x5385 runnable"GC task thread#3 (ParallelGC)" prio=10 tid=0x0947a000 nid=0x5387 runnable"VM Periodic Task Thread" prio=10 tid=0x09489800 nid=0x5391 waiting on conditionJNI global references: 854HeapPSYoungGen      total 14976K, used 1029K [0xa2dd0000, 0xa3e80000, 0xb39d0000)eden space 12864K, 8% used [0xa2dd0000,0xa2ed1530,0xa3a60000)from space 2112K, 0% used [0xa3c70000,0xa3c70000,0xa3e80000)to   space 2112K, 0% used [0xa3a60000,0xa3a60000,0xa3c70000)PSOldGen        total 34304K, used 0K [0x815d0000, 0x83750000, 0xa2dd0000)object space 34304K, 0% used [0x815d0000,0x815d0000,0x83750000)PSPermGen       total 16384K, used 1739K [0x7d5d0000, 0x7e5d0000, 0x815d0000)object space 16384K, 10% used [0x7d5d0000,0x7d782e90,0x7e5d0000)

参考: 有益的CountDownLatch和我们的JCG合作伙伴 Adrianos Dadis 遇到的棘手的Java僵局 ,位于“ Java,集成和源代码的美德”博客上 。

相关文章 :
  • Java并发教程– CountDownLatch
  • 并发优化–减少锁粒度
  • Java内存模型–快速概述和注意事项
  • Java并发教程–线程池
  • Java并发教程–信号量
  • Java教程和Android教程列表

翻译自: https://www.javacodegeeks.com/2011/11/beneficial-countdownlatch-and-tricky.html

有益的CountDownLatch和棘手的Java死锁相关推荐

  1. java 分析java死锁_有益的CountDownLatch和棘手的Java死锁

    java 分析java死锁 您是否曾经使用过java.util.concurrent.CountDownLatch ? 这是在两个或多个线程之间实现同步的非常方便的类,在该类中,一个或多个线程可以等待 ...

  2. 这些棘手的Java面试题,答案你都知道吗?

    转载自  这些棘手的Java面试题,答案你都知道吗? 棘手的Java面试问题是那些有一些惊喜元素的问题.如果你试图用常识回答一个棘手的问题,你很可能会因为需要一些特定的知识而失败.大多数棘手的Java ...

  3. 一道非常棘手的 Java 面试题:i++ 是线程安全的吗

    转载自  一道非常棘手的 Java 面试题:i++ 是线程安全的吗 i++ 是线程安全的吗? 相信很多中高级的 Java 面试者都遇到过这个问题,很多对这个不是很清楚的肯定是一脸蒙逼.内心肯定还在质疑 ...

  4. java线程死锁 cpu 100%_一文学会Java死锁和CPU 100% 问题的排查技巧

    做一个积极的人 编码.改bug.提升自己 我有一个乐园,面向编程,春暖花开 工欲善其事,必先利其器 00 本文简介 作为一名搞技术的程序猿或者是攻城狮,想必你应该是对下面这两个问题有所了解,说不定你在 ...

  5. java 分析java死锁_Java死锁示例–如何分析死锁情况

    java 分析java死锁 死锁是两个线程或多个线程永远被阻塞的编程情况,这种情况发生在至少两个线程和两个或更多资源的情况下. 在这里,我编写了一个简单的程序,它将导致死锁情况,然后我们将看到如何对其 ...

  6. java 死锁的检测与修复_调查死锁–第4部分:修复代码

    java 死锁的检测与修复 在这个简短的博客系列的最后BadTransferOperation中,我一直在讨论分析死锁,我将修复BadTransferOperation代码. 如果您已经看过本系列的其 ...

  7. java 死锁 解决_Java死锁故障排除和解决

    java 死锁 解决 JavaOne年度会议的一大优点是主题专家介绍了几个技术和故障排除实验室. 这些实验室之一尤其引起了我的关注:Java冠军Heinz Kabutz提出的" HOL650 ...

  8. Java死锁示例–如何分析死锁情况

    死锁是两个或多个线程永远被阻塞的编程情况,这种情况发生在至少两个线程和两个或更多资源的情况下. 在这里,我编写了一个简单的程序,该程序将导致死锁情况,然后我们将看到如何对其进行分析. Java死锁示例 ...

  9. Java死锁故障排除和解决

    JavaOne年度会议的一大优点是,主题专家介绍了几个技术和故障排除实验室. 其中的一个实验室今年特别吸引了我的注意力:" HOL6500-查找和解决Java死锁 ",由Java冠 ...

最新文章

  1. 买衣服送粉丝,人气美女出新招
  2. posix_kill 信号
  3. 如何保护你的 Python 代码—— 定制 Python 解释器
  4. boost::filesystem模块打印文件的大小的测试程序
  5. 推荐一个yaml文件转json文件的在线工具
  6. STM8L芯片启动时钟分频问题及发现(转)
  7. JavaWeb学习笔记-目录(待完成)
  8. springmvc从request中获取body的数据的方法
  9. phpstudy之解决phpmyadmin卡顿的方法
  10. (高级)Matlab绘制中国地图超全教程详解
  11. 刚刚,5000000 里程碑达成!
  12. 大话西游中的这些经典元素,注意到没?
  13. 测试软件的稳定性,客户端稳定性测试
  14. 《HarmonyOS实战—交互的艺术》
  15. 什么?HomeKit、米家、Aqara等生态也能通过智汀与天猫精灵生态联动?
  16. 如何实现不规则的Div外部形状?
  17. DES方式加解密的简单介绍
  18. VMware宿主机与虚拟机之间的文件夹相互共享(看图操作)
  19. 转载——如何让自己像打王者荣耀一样发了疯、拼了命、石乐志的学习?
  20. SoundPlayer 播放无声音解决方案

热门文章

  1. 优先队列——二项队列(binominal queue)
  2. lambda 匿名内部类_Lambda运行时内部:窥视无服务器巢穴
  3. aws lambda_跑来跑去:假人与AWS Lambda的第一次接触
  4. 简单工厂抽象工厂工厂方法_让工厂美丽
  5. jboss fuse 教程_JBoss Fuse –使用MVEL将您的静态配置转换为动态模板
  6. gradle的二进制版本_Gradle入门:创建二进制分发
  7. 命令模式应用_命令设计模式的应用
  8. 带有HttpClient的自定义HTTP标头
  9. 使用Jenkins,GitHub和Docker的最先进的持续集成和部署管道
  10. karaf osgi_在OSGi中为Karaf构建Camel-CXF REST服务–组播和聚合