Java线程死锁–案例研究

本文将描述从在IBM JVM 1.6上运行的Weblogic 11g生产系统中观察到的最新Java死锁问题的完整根本原因分析。

此案例研究还将证明掌握线程转储分析技能的重要性; 包括用于IBM JVM Thread Dump格式。

环境规格

– Java EE服务器:Oracle Weblogic Server 11g和Spring 2.0.5 –操作系统:AIX 5.3 – Java虚拟机:IBM JRE 1.6.0 –平台类型:门户和订购应用程序
监控和故障排除工具
– JVM线程转储(IBM JVM格式)– Compuware Server Vantage(Weblogic JMX监视和警报)

问题概述

从Compuware Server Vantage观察到并报告了一个严重的线程阻塞问题,该问题影响了我们的2台Weblogic 11g生产托管服务器,从而导致了最终用户的应用程序影响和超时情况。

事实的收集和验证

像往常一样,Java EE问题调查需要收集技术和非技术事实,因此我们可以得出其他事实和/或就根本原因进行结论。 在采取纠正措施之前,要对以下事实进行核实,以便得出根本原因:
·对客户有什么影响? MEDIUM(16个中只有2个受管服务器/ JVM受影响)·受影响平台的最新更改? 是(新的与JMS相关的异步组件)·最近到受影响平台的流量有增加吗? 否·这个问题如何表现出来? 观察到线程突然增加,导致线程快速耗尽。·Weblogic托管服务器重新启动是否解决了问题? 是的,但是几个小时后问题又回来了(不可预测的间歇性模式)

结论1

该问题与间歇性卡住的线程行为有关,该行为当时仅影响少数Weblogic托管服务器

结论2

由于问题是断断续续的,因此不太可能出现全局根本原因,例如下游系统无响应

线程转储分析–第一遍

处理滞留的线程问题时,要做的第一件事是生成JVM线程转储。 无论您的环境规格和问题背景如何,这都是一条黄金法则。 JVM线程转储快照为您提供了有关活动线程及其当时正在执行的处理/任务类型的重要信息。
现在回到我们的案例研究,生成了一个IBM JVM线程转储(javacore.xyz格式),它确实揭示了以下Java线程死锁情况:

1LKDEADLOCK    Deadlock detected !!!
             
             
             NULL           ---------------------
             
             
             NULL           
             
             
             2LKDEADLOCKTHR  Thread '[STUCK] ExecuteThread: '8' for queue: 'weblogic.kernel.Default (self-tuning)'' (0x000000012CC08B00)
             
             
             3LKDEADLOCKWTR    is waiting for:
             
             
             4LKDEADLOCKMON      sys_mon_t:0x0000000126171DF8 infl_mon_t: 0x0000000126171E38:
             
             
             4LKDEADLOCKOBJ      weblogic/jms/frontend/FESession@0x07000000198048C0/0x07000000198048D8: 
             
             
             3LKDEADLOCKOWN    which is owned by:
             
             
             2LKDEADLOCKTHR  Thread '[STUCK] ExecuteThread: '10' for queue: 'weblogic.kernel.Default (self-tuning)'' (0x000000012E560500)
             
             
             3LKDEADLOCKWTR    which is waiting for:
             
             
             4LKDEADLOCKMON      sys_mon_t:0x000000012884CD60 infl_mon_t: 0x000000012884CDA0:
             
             
             4LKDEADLOCKOBJ      weblogic/jms/frontend/FEConnection@0x0700000019822F08/0x0700000019822F20: 
             
             
             3LKDEADLOCKOWN    which is owned by:
             
             
             2LKDEADLOCKTHR  Thread '[STUCK] ExecuteThread: '8' for queue: 'weblogic.kernel.Default (self-tuning)'' (0x000000012CC08B00)

死锁情况可以按照以下方式进行翻译:
– Weblogic线程8正在等待获取Weblogic线程10拥有的对象监视器锁
– Weblogic线程#10正在等待获取Weblogic线程#8拥有的对象监视器锁 结论: Weblogic线程#8和#10都在等待。 永远! 现在,在深入分析根本原因之前,让我为您提供有关Java Thread死锁的高级概述。

Java线程死锁概述

你们中的大多数人可能都熟悉Java Thread死锁原理,但是您真的遇到了真正的死锁问题吗?

根据我的经验,真正的Java死锁很少见,在过去的10年中,我只看到5次此类死锁。 原因是大多数与线程卡住有关的问题是由于线程挂起情况(正在等待远程IO调用等)引起的,而与其他线程没有真正的死锁条件有关。
Java线程死锁是一种情况,例如,线程A等待获取线程B持有的对象监视器锁,而该线程本身正等待获取线程A持有的对象监视器锁。这两个线程将永远彼此等待。 这种情况可以如下图所示:

线程死锁已确认……现在该怎么办?
一旦确认了死锁( 大多数JVM线程转储实现将为您突出显示 ),下一步就是通过检查死锁情况下涉及的每个线程及其当前任务和等待条件,来进行更深入的分析。
在我们的问题案例中,对于涉及死锁条件的每个线程,在部分线程堆栈跟踪下面找到: **请注意,出于保密目的,真实应用程序Java包名称已重命名**

Weblogic线程#8

'[STUCK] ExecuteThread: '8' for queue: 'weblogic.kernel.Default (self-tuning)'' J9VMThread:0x000000012CC08B00, j9thread_t:0x00000001299E5100, java/lang/Thread:0x070000001D72EE00, state:B, prio=1
             
             
             (native thread ID:0x111200F, native priority:0x1, native policy:UNKNOWN)
             
             
             Java callstack:
             
             
                    at weblogic/jms/frontend/FEConnection.stop(FEConnection.java:671(Compiled Code))
             
             
                    at weblogic/jms/frontend/FEConnection.invoke(FEConnection.java:1685(Compiled Code))
             
             
                    at weblogic/messaging/dispatcher/Request.wrappedFiniteStateMachine(Request.java:961(Compiled Code))
             
             
                    at weblogic/messaging/dispatcher/DispatcherImpl.syncRequest(DispatcherImpl.java:184(Compiled Code))
             
             
                    at weblogic/messaging/dispatcher/DispatcherImpl.dispatchSync(DispatcherImpl.java:212(Compiled Code))
             
             
                    
             at weblogic/jms/dispatcher/DispatcherAdapter.dispatchSync(DispatcherAdapter.java:43(Compiled Code))
             
             
                    
             at weblogic/jms/client/JMSConnection.stop(JMSConnection.java:863(Compiled Code))
             
             
                    at weblogic/jms/client/WLConnectionImpl.stop(WLConnectionImpl.java:843)
             
             
                    at org/springframework/jms/connection/SingleConnectionFactory.closeConnection(SingleConnectionFactory.java:342)
             
             
                    at org/springframework/jms/connection/SingleConnectionFactory.resetConnection (SingleConnectionFactory.java:296)
             
             
                    at org/app/JMSReceiver.receive()
             
             
             ……………………………………………………………………
             
            

Weblogic线程#10

'[STUCK] ExecuteThread: '10' for queue: 'weblogic.kernel.Default (self-tuning)'' J9VMThread:0x000000012E560500, j9thread_t:0x000000012E35BCE0, java/lang/Thread:0x070000001ECA9200, state:B, prio=1
             
             
              (native thread ID:0x4FA027, native priority:0x1, native policy:UNKNOWN)
             
             
             Java callstack:
             
             
                    at weblogic/jms/frontend/FEConnection .getPeerVersion(FEConnection.java:1381(Compiled Code))
             
             
                    at weblogic/jms/frontend/FESession.setUpBackEndSession(FESession.java:755(Compiled Code))
             
             
                    at weblogic/jms/frontend/FESession.consumerCreate(FESession.java:1025(Compiled Code))
             
             
                    at weblogic/jms/frontend/FESession.invoke(FESession.java:2995(Compiled Code))
             
             
                    at weblogic/messaging/dispatcher/Request.wrappedFiniteStateMachine(Request.java:961(Compiled Code))
             
             
                    at weblogic/messaging/dispatcher/DispatcherImpl.syncRequest(DispatcherImpl.java:184(Compiled Code))
             
             
                    at weblogic/messaging/dispatcher/DispatcherImpl.dispatchSync(DispatcherImpl.java:212(Compiled Code))
             
             
                    
             at weblogic/jms/dispatcher/DispatcherAdapter.dispatchSync(DispatcherAdapter.java:43(Compiled Code))
             
             
                    
             at weblogic/jms/client/JMSSession.consumerCreate(JMSSession.java:2982(Compiled Code))
             
             
                    at weblogic/jms/client/JMSSession.setupConsumer(JMSSession.java:2749(Compiled Code))
             
             
                    at weblogic/jms/client/JMSSession.createConsumer(JMSSession.java:2691(Compiled Code))
             
             
                    at weblogic/jms/client/JMSSession.createReceiver(JMSSession.java:2596(Compiled Code))
             
             
                    at weblogic/jms/client/WLSessionImpl.createReceiver(WLSessionImpl.java:991(Compiled Code))
             
             
                    at org/springframework/jms/core/JmsTemplate102.createConsumer(JmsTemplate102.java:204(Compiled Code))
             
             
                    at org/springframework/jms/core/JmsTemplate.doReceive(JmsTemplate.java:676(Compiled Code))
             
             
                    at org/springframework/jms/core/JmsTemplate$10.doInJms(JmsTemplate.java:652(Compiled Code))
             
             
                    at org/springframework/jms/core/JmsTemplate.execute(JmsTemplate.java:412(Compiled Code))
             
             
                    at org/springframework/jms/core/JmsTemplate.receiveSelected(JmsTemplate.java:650(Compiled Code))
             
             
                    at org/springframework/jms/core/JmsTemplate.receiveSelected(JmsTemplate.java:641(Compiled Code))
             
             
                    at org/app/JMSReceiver.receive()
             
             
             ……………………………………………………………
     

如您在上面的“线程跟踪跟踪”中所看到的,这种死锁确实来自我们的应用程序代码,该应用程序代码使用Spring框架API进行JMS使用者实现(在不使用MDB的情况下非常有用)。 堆栈跟踪非常有趣,它揭示了两个线程都在与相同的 Weblogic JMS使用者会话/连接竞争的情况下,并导致死锁情况:
– Weblogic线程#8试图重置关闭当前的JMS连接– Weblogic线程#10试图使用相同的JMS连接/会话以创建新的JMS使用方–触发了线程死锁!

根本原因:非线程安全的Spring JMS SingleConnectionFactory实现

Spring JIRA错误数据库的代码回顾和快速研究确实揭示了以下与上述分析完美相关的以下线程安全缺陷:
#SingleConnectionFactory的resetConnection导致与基础OracleAQ的JMS连接的死锁https://jira.springsource.org/browse/SPR-5987
Spring SingleConnectionFactory的修补程序于2009年发布,确实涉及添加适当的sync {}块,以防止在发生JMS Connection重置操作时线程死锁:

synchronized (connectionMonitor) {
             
             
               //if condition added to avoid possible deadlocks when trying to reset the target connection 
             
             
               if (!started) {
             
             
                 this.target.start(); 
             
             
                 started = true; 
             
             
               }
             
             
             }

我们的团队目前正计划将该Spring补丁不久后集成到我们的生产环境中。 在我们的测试环境中执行的初始测试是肯定的。

结论

我希望这个案例研究能帮助您理解现实中的Java线程死锁问题,以及适当的线程转储分析技能如何使您能够在代码级快速查明与线程相关的卡住问题的根本原因。 请不要犹豫,发表任何评论或问题。

参考: Java Thread死锁–来自我们的JCG合作伙伴 Pierre-Hugues Charbonneau的案例研究 ,位于Java EE支持模式和Java教程博客上。


翻译自: https://www.javacodegeeks.com/2012/06/java-thread-deadlock-case-study.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值