服务器挂起 - 应用程序死锁

问题描述
应用程序代码中的疏忽性死锁可导致服务器挂起。例如,有这样一种情况:thread1 在等待 resource1 时仍保持对 resource2 的锁定,而 thread2 需要使用 resource2,但仍保持对 resource1 的锁定。结果是两个线程都无法继续执行。

故障排除
只有在执行常规服务器挂起模式中的所有步骤后故障依旧时,才应使用此“应用程序死锁”模式。可以说明这种情况是应用程序死锁故障的一个标志是,Thread Dump 显示线程在应用程序方法中。如果以几秒钟的间隔进行几次 Thread Dump,结果将显示线程没有继续执行。要排除该故障,需要检查应用程序代码。BEA dev2dev 上提供了一个线程分析器工具,事实证明该工具在 Thread Dump 分析上很有帮助。

快速链接

为什么发生此问题?
发生此问题的根本原因是,在设计和实现应用程序的过程中引入了可能导致死锁的因素。此类问题可能在负载量大的情况下才会显现出来。因此,这些应用程序往往可以通过 QA 测试,却会在生产过程中发生问题。

可以着手查找的编码问题:

  • 不必要地使用同步 java 类。例如,使用 Hashtable(同步),而不使用 HashMap(非同步)
  • 应用程序有包含同步对象方法调用的同步方法。请参阅下面的示例代码。

import java.util.Vector;   <-- Vector is a synchronized java class
Public class Employee {
           Vector names = new Vector();
            Employees () {
                   Object object = new Object();
                    synchronized (object) {
                            names.add("al");
                            names.add("Saganich");
                     }
            }
            synchronized String getName (int index) {
                      String name = (String) names.elementAt(index);
                       return name;
            }
}

  • 对长时间运行的复杂代码使用同步。
  • 线程等待的是永远无法变为可用的资源。
应用程序设计
  • 应用程序用尽了配置的所有线程。如果正在执行的线程到达某一点时必须等待另一线程完成的工作才能继续,就会发生这种情况。在时间上,可能是此线程正在尝试进入的被等待的方法的运行时间长。最终,所有线程都必须到达此长时间运行的方法。运行一段时间后,应用程序会发现线程在排队等待此长时间运行的方法。由于分配的线程数悉数用尽,因此无法引入新的工作。请参阅下面的示例代码。

import java.....;
import java.....;

public class myAppMethods {

????????? public String getName(String name) {
                     String lastname =  getLastName(name);
                     return lastname;
          }
          public synchronized String getLastName (String name) {
                     do a DB Lookup    <------------ takes mucho time to get a last name
                      return lastname;
         }
}

如果数据库运行速度很慢,服务器会表现出挂起的样子,因为尝试访问数据库的线程会排成队列,最终会用尽所有可用的线程。

  • 在 WebLogic Server 内运行的应用程序调用远程计算机上的另一个 WebLogic 实例上的服务。调用的远程计算机上的远程服务会向第一个服务器发出回调。这就为第一个服务器发生死锁制造了机会,特别是在负载量大的情况下。第一个服务器有一个执行线程,该线程因等待返回响应而暂停执行。该返回响应需要的线程与等待接收响应的线程须来自同一个执行池。
如果第一个服务器的运行速度比远程服务器快,最终发出向外请求的服务器将用尽执行池中的所有线程,只剩下少数线程可用于处理返回响应。随着负载增大,由于要等待返回响应完成,无法完成工作的出站请求的数量也随之增加。下面是 waitForDataResponseImpl.java 方法中的一个线程的示例代码。

"ExecuteThread: '52' for queue: 'default'" daemon prio=5 tid=0x4b3e40b0 nid=0x1170 waiting on monitor [0x4c74f000..0x4c74fdbc] 
at java.lang.Object.wait(Native Method)
at 
weblogic.rjvm.ResponseImpl.waitForData(ResponseImpl.java:72)


已知的 WebLogic Server 问题
Weblogic Server 无法检测死锁的线程。某些 JVM 能够检测此类线程。请参阅外部资源。BEA dev2dev 上提供了一个用于线程分析的工具和一些有关 Thread Dump 的有用信息。


返回页首


外部资源
如果您怀疑发生了死锁,一种有帮助的做法是:访问 JVM 供应商的网站,了解他们的 Thread Dump 中是否提供了解决您所遇到的问题的线索。如果您使用的是 JDK 1.3.1,则可以添加 - XX:+JavaMonitorInStack Trace 来以更显式/内置的方式显示锁定情况。下面是一个 HP JVM Thread Dump 的示例,其中清楚地标示出线程的状态是等待锁,并标识了锁。请参阅下面的示例代码。

"msg 0-941667944865" (TID:0x7b1a5ba0, sys_thread_t:0x2a4290,
  state:Waiting on Monitor,
  thread_t: t@108, stack_base:0x7a76e000, stack_size:0x20000,
 pc: 0xc01ea178, monitor = 0x25334) prio=8
  MsgThread.rest(Compiled Code)
  MsgThread.run(Compiled Code)
"msg 3-941667944865" (TID:0x7b1a5a80, sys_thread_t:0x263278,
  state:Waiting on Monitor,
  thread_t: t@105, stack_base:0x7a95d000, stack_size:0x20000,
 pc: 0xc01ea178, monitor = 0x25334) prio=8
  MsgThread.rest(Compiled Code)
  MsgThread.run(Compiled Code)
"msg 5-941667944864" (TID:0x7b1a5f08, sys_thread_t:0x2c42d8,
  state:Waiting on Monitor,
  thread_t: t@106, stack_base:0x7aa65000, stack_size:0x20000,
 pc: 0xc01ea178, monitor = 0x25334) prio=8
  MsgThread.rest(Compiled Code)
  MsgThread.run(Compiled Code)


返回页首

需要更多帮助?
如果您已经理解这个模式,但仍需要更多帮助,您可以:
  1. http://support.bea.com/ 上查询 AskBEA(例如,使用“server hang”),以查找其它已发布的解决办法。
  2. http://forums.bea.com 上,向 BEA 的某个新闻组提出更详细具体的问题。
如果这还不能解决您的问题,并且您拥有有效的技术支持合同,您可以通过登录以下网站来打开支持案例:http://support.bea.com/

反馈

请给我们提供您的意见,说明此支持诊断模式“服务器挂起 - 应用程序死锁”一文是否有所帮助、您需要的任何解释,以及对支持诊断模式的新主题的任何要求。


免责声明:

依据 BEA 与您签署的维护和支持协议条款,BEA Systems, Inc. 在本网站上提供技术技巧和补丁供您使用。虽然您可以将这些信息和代码与您获得 BEA 授权的软件一起使用,但 BEA 并不对所提供的技术技巧和补丁做任何形式的担保,无论是明确的还是隐含的。

本文档中引用的任何商标是其各自所有者的财产。有关完整的商标信息,请参考您的产品手册。