地方门户网站如何推广,宁波建站推广技术公司,上海工商信息查询官网,天长网站设计java线程死锁大多数Java程序员熟悉Java线程死锁概念。 它本质上涉及2个线程#xff0c;它们彼此永远等待。 这种情况通常是平面#xff08;同步#xff09;或ReentrantLock#xff08;读或写#xff09;锁排序问题的结果。 Found one Java-level deadlock:pool-1-t… java线程死锁 大多数Java程序员熟悉Java线程死锁概念。 它本质上涉及2个线程它们彼此永远等待。 这种情况通常是平面同步或ReentrantLock读或写锁排序问题的结果。 Found one Java-level deadlock:pool-1-thread-2:waiting to lock monitor 0x0237ada4 (object 0x272200e8, a java.lang.Object),which is held by pool-1-thread-1
pool-1-thread-1:waiting to lock monitor 0x0237aa64 (object 0x272200f0, a java.lang.Object),which is held by pool-1-thread-2 好消息是HotSpot JVM始终可以为您检测到这种情况……还是吗 最近一个影响Oracle Service Bus生产环境的线程死锁问题迫使我们重新审视此经典问题并确定“隐藏”死锁情况的存在。 本文将通过一个简单的Java程序演示并复制非常特殊的锁顺序死锁条件最新的HotSpot JVM 1.7并未检测到该情况。 您还可以在本文结尾处找到一个视频 向您介绍Java示例程序以及所使用的故障排除方法。 犯罪现场 我通常喜欢将主要的Java并发问题与犯罪现场进行比较在犯罪现场您扮演首席调查员的角色。 在这种情况下“犯罪”是客户IT环境的实际生产中断。 您的工作是 收集所有证据提示和事实线程转储日志业务影响负载数字… 询问证人和领域专家支持团队交付团队供应商客户... 调查的下一步是分析收集的信息并建立一个或多个“嫌疑人”的潜在清单以及明确的证据。 最终您希望将其范围缩小到主要可疑或根本原因。 显然“直到证明有罪之前无罪”的法律在这里并不适用恰恰相反。 缺乏证据可能会阻止您实现上述目标。 接下来您将看到Hotspot JVM缺少死锁检测并没有必要证明您没有解决此问题。 犯罪嫌疑人 在此故障排除上下文中“可疑”定义为具有以下有问题的执行模式的应用程序或中间件代码。 使用FLAT锁然后使用ReentrantLock WRITE锁执行路径1 使用ReentrantLock READ锁然后使用FLAT锁执行路径2 由2个Java线程并发执行但执行顺序相反 上面的锁排序死锁标准可以如下所示 现在让我们通过示例Java程序来复制此问题并查看JVM线程转储输出。 示例Java程序 上面的死锁条件是首先从我们的Oracle OSB问题案例中发现的。 然后我们通过一个简单的Java程序重新创建了它。 您可以在此处 下载我们程序的完整源代码。 该程序只是创建并触发2个工作线程。 它们每个执行不同的执行路径并尝试以不同的顺序获取共享对象上的锁。 我们还创建了一个死锁检测器线程以进行监视和记录。 现在在下面找到实现2条不同执行路径的Java类。 package org.ph.javaee.training8;import java.util.concurrent.locks.ReentrantReadWriteLock;/*** A simple thread task representation* author Pierre-Hugues Charbonneau**/
public class Task {// Object used for FLAT lockprivate final Object sharedObject new Object();// ReentrantReadWriteLock used for WRITE READ locksprivate final ReentrantReadWriteLock lock new ReentrantReadWriteLock();/*** Execution pattern #1*/public void executeTask1() {// 1. Attempt to acquire a ReentrantReadWriteLock READ locklock.readLock().lock();// Wait 2 seconds to simulate some work...try { Thread.sleep(2000);}catch (Throwable any) {}try { // 2. Attempt to acquire a Flat lock...synchronized (sharedObject) {}}// Remove the READ lockfinally {lock.readLock().unlock();} System.out.println(executeTask1() :: Work Done!);}/*** Execution pattern #2*/public void executeTask2() {// 1. Attempt to acquire a Flat locksynchronized (sharedObject) { // Wait 2 seconds to simulate some work...try { Thread.sleep(2000);}catch (Throwable any) {}// 2. Attempt to acquire a WRITE lock lock.writeLock().lock();try {// Do nothing}// Remove the WRITE lockfinally {lock.writeLock().unlock();}}System.out.println(executeTask2() :: Work Done!);}public ReentrantReadWriteLock getReentrantReadWriteLock() {return lock;}
} 一旦触发死锁情况就会使用JVisualVM生成JVM线程转储。 从Java线程转储示例中可以看到。 JVM没有检测到此死锁条件例如不存在“发现一个Java级死锁”但是很明显这两个线程处于死锁状态。 根本原因ReetrantLock READ锁定行为 至此我们发现的主要解释与ReetrantLock READ锁的用法有关。 读取锁通常不设计为具有所有权概念。 由于没有哪个线程持有读取锁的记录因此这似乎可以防止HotSpot JVM死锁检测器逻辑检测到涉及读锁的死锁。 从那时起就实现了一些改进但是我们可以看到JVM仍然无法检测到这种特殊的死锁情况。 现在如果我们用写锁替换程序中的读锁执行模式2那么JVM将最终检测到死锁情况但是为什么呢 Found one Java-level deadlock:pool-1-thread-2:waiting for ownable synchronizer 0x272239c0, (a java.util.concurrent.locks.ReentrantReadWriteLock$NonfairSync),which is held by pool-1-thread-1
pool-1-thread-1:waiting to lock monitor 0x025cad3c (object 0x272236d0, a java.lang.Object),which is held by pool-1-thread-2Java stack information for the threads listed above:pool-1-thread-2:at sun.misc.Unsafe.park(Native Method)- parking to wait for 0x272239c0 (a java.util.concurrent.locks.ReentrantReadWriteLock$NonfairSync)at java.util.concurrent.locks.LockSupport.park(LockSupport.java:186)at java.util.concurrent.locks.AbstractQueuedSynchronizer.
parkAndCheckInterrupt(AbstractQueuedSynchronizer.java:834)at java.util.concurrent.locks.AbstractQueuedSynchronizer.
acquireQueued(AbstractQueuedSynchronizer.java:867)at java.util.concurrent.locks.AbstractQueuedSynchronizer.
acquire(AbstractQueuedSynchronizer.java:1197)at java.util.concurrent.locks.ReentrantReadWriteLock$WriteLock.lock(ReentrantReadWriteLock.java:945)at org.ph.javaee.training8.Task.executeTask2(Task.java:54)- locked 0x272236d0 (a java.lang.Object)at org.ph.javaee.training8.WorkerThread2.run(WorkerThread2.java:29)at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1110)at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:603)at java.lang.Thread.run(Thread.java:722)
pool-1-thread-1:at org.ph.javaee.training8.Task.executeTask1(Task.java:31)- waiting to lock 0x272236d0 (a java.lang.Object)at org.ph.javaee.training8.WorkerThread1.run(WorkerThread1.java:29)at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1110)at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:603)at java.lang.Thread.run(Thread.java:722) 这是因为JVM跟踪写入锁类似于平面锁。 这意味着HotSpot JVM死锁检测器似乎当前被设计用来检测 对象监视器上涉及FLAT锁的死锁 死锁涉及与WRITE锁关联的锁定的可拥有同步器 缺少读取锁每线程跟踪似乎可以防止这种情况下的死锁检测并大大增加了故障排除的复杂性。 我建议您阅读Doug Lea在整个问题上的评论 因为早在2005年人们就开始担心由于某些潜在的锁定开销而可能增加按线程读取保持跟踪的可能性。 如果您怀疑涉及读取锁的隐藏死锁情况请在下面的故障排除建议中查找 仔细分析线程调用堆栈跟踪它可能会揭示某些代码潜在地获取读锁并阻止其他线程获取写锁。 如果您是代码的所有者请通过使用lock.getReadLockCount方法来跟踪读取锁的计数。 我期待着您的反馈特别是对于具有此类涉及读锁的死锁经验的个人。 最后在下面的视频中找到通过示例Java程序的执行和监视来解释这些发现的视频。 参考 Java并发性 Java EE支持模式和Java教程博客中我们的JCG合作伙伴 Pierre-Hugues Charbonneau 隐藏的线程死锁 。 翻译自: https://www.javacodegeeks.com/2013/02/java-concurrency-the-hidden-thread-deadlocks.htmljava线程死锁