当两个或多个线程彼此形成循环依赖关系时,就出现了死锁。例如,如果线程 A 处于等待线程 B 的等待状态,而同时线程 B 处于等待线程 A 的等待状态,则出现了死锁。一旦形成此情况,线程 A 和线程 B 都不能有任何进展,因为这两个线程现在都无限期地挂起了。为什么会有人创建这种系统?当然,您并不会有意这么做,但由于存在大量线程和复杂事务,因此很容易出现这种情况。
本文将介绍如何使用 IBM WebSphere Application Server V6.1 的线程转储工具来对系统进行检查,以确定是否出现了死锁。为了帮助理解,文中引用和描述了一个真实的示例。在此示例中,企业应用程序在 Web 容器中运行,而 OSGi 包提供对协议的访问。当通信流量突然上升到足够的程度时,在服务器上运行的应用程序将挂起,而且情况极为严重,唯一能够执行的任务就是发出 kill -9 命令来终止进程。本文将介绍如何发现这个典型的死锁情况并加以解决,其中涉及的方法可用于避免或调试您应用程序中类似的条件。
问题
在很多协议中,使用了状态机来管理每个协议连接的状态。这些状态机有时候称为连接有限状态机(Connection Finite State Machines,CFSM)。状态机需要原子操作。这意味着无法在不同的时间对状态机的不同部分进行更新;出现更新时,必须在该操作中对整个状态机进行更新,因此下一个操作应用到新状态,以此类推。为了达到这样的方式,状态机要设计为任何时间只有一个线程能够对状态机执行操作。对状态机的访问方法全部是同步的。希望访问状态机的线程在当前访问状态机的线程完成方法调用前将被阻塞。
在此类示例协议中,任何时间收到、传输数据包或操作超时,都会更改状态。其他条件(如启动、停止和断开连接)也会导致状态机发生变化。状态机的这些输入使其成为了搜索的高风险区,特别是任意数量的线程都可能导致这些事件发生的情况下更是如此。
首次在此应用程序中出现这个问题时,应用服务器锁定情况非常严重,没有出现日志记录,甚至控制台都不能打开。实际上,WebSphere Application Server 进程没有 TCP 通信流量进出。这是死锁的典型症状。CPU 利用率为零,未发生任何事件,也没有请求被接收。唯一可用于进行调试的工具是 WebSphere Application Server 进程的线程转储。
线程转储(或核心文件)是采用以下格式的名称生成的:
javacore.date.time.id.txt
例如: javacore.20070919.204717.27050.txt
在有些情况下,会自动为 Java™ Virtual Machine (JVM) 创建线程转储,如 WebSphere Application Server 由于普通 stopserver 请求之外的其他方式停止的情况。线程转储还可以通过向 WebSphere Application Server 进程发出信号来触发。例如,为了在 UNIX® 环境中生成线程转储,则可以运行此命令:
kill -3 process_id
其中 process_id 是 WebSphere Application Server JVM 的进程 ID。线程转储还可以使用 wsadmin 创建。为了使用 wsadmin 命令提示符强制进行线程转储,请发出以下命令:
wsadmin
wsadmin>set jvm [$AdminControl completeObjectName type=JVM, process=server1,*]
wsadmin>$AdminControl invoke $jvm dumpThreads
这将在 was_profile_root 中创建类似于 javacore.20071012.080508.4252.txt 的文件。
打开线程转储时会看到以下内容:WebSphere Application Server 中每个单线程的堆栈跟踪。但更为重要的是,其中还列出了系统中的所有锁定。很好!
线程转储内容
线程转储是简单的文本文件,可以使用任何文本编辑器打开。您可以在其中找到大量有意义而且有用的信息,具体请参见后面的描述。
环境数据
转储首先提供的是关于 WebSphere Application Server 进程的运行环境的一些信息(清单 1).其中描述操作系统级别、JRE 级别、处理器数量等等。这些信息很不错,不过与死锁并非真的相关。
清单 1
NULL ------------------------------------------------------------------------
0SECTION TITLE subcomponent dump routine
NULL ===============================
1TISIGINFO Dump Event "user" (00004000) received
1TIDATETIME Date: 2007/09/19 at 20:47:17
1TIFILENAME Javacore filename:
/usr/IBM/WebSphere/AppServer/profiles/AppSrv01/javacore.20070919.204717.27050.txt
NULL ------------------------------------------------------------------------
0SECTION GPINFO subcomponent dump routine
NULL ================================
2XHOSLEVEL OS Level : AIX 5.3
2XHCPUS Processors -
3XHCPUARCH Architecture : ppc
3XHNUMCPUS How Many : 4
NULL
1XHERROR2 Register dump section only produced for SIGSEGV, SIGILL or SIGFPE.
NULL
NULL ------------------------------------------------------------------------
0SECTION ENVINFO subcomponent dump routine
NULL =================================
1CIJAVAVERSION J2RE 5.0 IBM J9 2.3 AIX ppc-32 build j9vmap3223-20070426
1CIVMVERSION VM build 20070420_12448_bHdSMR
1CIJITVERSION JIT enabled - 20070419_1806_r8
1CIRUNNINGAS Running as a standalone JVM
|
内存数据
您可能并不急需内存转储信息,不过可能会关心堆信息。清单 2 说明堆上有大量空间可用,这表明不存在内存泄漏——或者至少当前内存泄漏不是最棘手的问题。您可以看到,可用空间的量约占 1 G 总空间的四分之三。
清单 2
0SECTION MEMINFO subcomponent dump routine
NULL =================================
1STHEAPFREE Bytes of Heap Space Free: 31c6fb90
1STHEAPALLOC Bytes of Heap Space Allocated: 40000000
NULL
|
锁定数据
锁定信息是查找死锁的关键。锁定是同时只能归一个线程所有的资源。在拥有锁定的线程释放锁定前,等待该锁定的其他线程会被阻塞。 The protocol example state machine is a monitor or lock, and is invoked on every packet transmitted or received on a protocol connection.协议示例状态机是一个监视器或锁定,将对协议连接上传输或收到的每个数据包进行调用。在这个特定的部分中,有三个协议线程和三个协议状态机。这意味着有三个协议连接已启动并在运行。这三个线程是:ProtocolThreadPool:0、ProtocolThreadPool:1 和 ProtocolThreadPool:2(线程取自名为“ProtocolThreadPool”的线程池,因此其名称如此)。
在清单 3 中,首先注意到的是 ProtocolThreadPool:0 和 2 在等待得到通知。这种情况很典型,协议线程阻塞等待读取传入数据包。不过,ProtocolThreadPool:1 正在处理一个接收数据包。从跟踪信息中,可以看到 ProtocolCfsm_Initiator(协议状态机)是 ProtocolThreadPool:1 所使用的监视器。这是一个特殊情况,在状态机中,协议线程接收到了数据包,并对其进行处理。所需的处理时间相当少,因为实际所执行的唯一工作就是查询将此数据包传递到何处。但是等待获得对 ProtocolCfsm_Initiator 监视器的访问的线程数量非常多!有 50 个 WorkManager 线程、34 个 WebContainer 线程和一个不可延期的 Alarm:0 线程。此时,您并不知道其等待的准确原因,但任何时间线程希望传输协议数据包时,都必须获得对特定连接的状态机的访问。因此,这些线程可以排队来等待传输数据包。
清单 3
NULL
NULL ------------------------------------------------------------------------
0SECTION LOCKS subcomponent dump routine
NULL ===============================
NULL
1LKPOOLINFO Monitor pool info:
2LKPOOLTOTAL Current total number of monitors: 968
2LKMONINUSE sys_mon_t:0x3461897C infl_mon_t: 0x346189A4:
3LKMONOBJECT java/lang/Object@A05B49B8/A05B49C4: <unowned> 3LKNOTIFYQ Waiting to be notified:
3LKWAITNOTIFY "ProtocolThreadPool: 0" (0x345FD800)
2LKMONINUSE sys_mon_t:0x346189D8 infl_mon_t: 0x34618A00:
3LKMONOBJECT java/lang/Object@A05AE930/A05AE93C: <unowned> 3LKNOTIFYQ Waiting to be notified:
3LKWAITNOTIFY "ProtocolThreadPool: 2" (0x3464C000)
2LKMONINUSE sys_mon_t:0x355C4E94 infl_mon_t: 0x355C4EBC: 3LKMONOBJECT com/ibm/protocol/cfsm/ProtocolCfsm_Initiator@A0456A00/A0456A0C: owner
"ProtocolThreadPool: 1" (0x3464BC00), entry count 1
3LKWAITERQ Waiting to enter:
3LKWAITER "WebContainer : 60" (0x32E01B00)
3LKWAITER "WebContainer : 61" (0x33045F00)
<... 32 more of these ...>
3LKWAITER "Non-deferrable Alarm : 0" (0x33846900)
3LKWAITER "WorkManager.ProtocolWorkManager : 0" (0x35895600)
3LKWAITER "WorkManager.ProtocolWorkManager : 1" (0x3580EB00)
<... 48 more of these ...>
2LKMONINUSE sys_mon_t:0x35815D30 infl_mon_t: 0x35815D58:
3LKMONOBJECT java/lang/Object@A0A3CF90/A0A3CF9C: <unowned> 3LKNOTIFYQ Waiting to be notified:
3LKWAITNOTIFY "ProtocolThreadPool : 1" (0x3464BC00)
|
另外请注意,ProtocolThreadPool:1 在 ProtocolCfsm_Initiator 监视器中,但也在等待通知。
线程堆栈跟踪
下一步是确定为何所有线程都在等待。线程转储的优势在于,它获取了所有线程堆栈的快照,提供了系统中每个线程的堆栈跟踪。转储的这个区域具有以下 Header:
清单 4
NULL
NULL ------------------------------------------------------------------------
0SECTION THREADS subcomponent dump routine
NULL =================================
NULL
1XMCURTHDINFO Current Thread Details
NULL ----------------------
NULL
1XMTHDINFO All Thread Details
NULL ------------------
|
根据上面的锁定,您可以检查哪些线程被阻塞,哪些线程在等待通知,或哪些线程允许进入监视器。第一个等待的线程是 WebContainer:62。您可以看到此线程正在处理 Web 服务请求 getCCServicePriceEnquiry(),而后者将调用 sendProtocolPacket()。如果仔细分析此方法在实际源代码实现中的代码行 (1196),将会发现所执行的代码行为:
cfsm.send(packet, realmName, packetCallback);
因此,线程将在 CFSM 上阻塞,等待进行传输。WebContainer:61 也在同一行代码上阻塞。唯一的区别在于,它在处理 sendCCRefund() Web 服务请求。不过,此任务最终会在尝试传输数据包时停止。如果看一下所有 WebContainer 线程(清单 5 和清单 6),会发现他们都在相同的代码行阻塞。
清单 5
3XMTHREADINFO "WebContainer : 62" (TID:0x33C13400, sys_thread_t:0x32FB4BA8, state:B,
native ID:0x0000C0B3) prio=5
4XESTACKTRACE at com/ibm/rotocol/base/ProtocolBaseApiHelper.sendProtocolPacket
(ProtocolBaseApiHelper.java:1196(Compiled Code))
4XESTACKTRACE at com/ibm/protocol/base/ProtocolBaseApiImpl.sendProtocolPacket
(ProtocolBaseApiImpl.java:649(Compiled Code))
4XESTACKTRACE at com/ibm/protocol/ro/ProtocolInterface.transmitPacket
(ProtocolInterface.java:68(Compiled Code))
4XESTACKTRACE at com/ibm/protocol/ro/CreditControlRequest.transmitPacket
(CreditControlRequest.java:390(Compiled Code))
4XESTACKTRACE at com/ibm/protocol/ro/CreditControlRequest.sendChargingInfo
(CreditControlRequest.java:147(Compiled Code))
4XESTACKTRACE at com/ibm/protocol/ro/ProtocolRoService.sendCCR
(ProtocolRoService.java:143(Compiled Code))
4XESTACKTRACE at com/ibm/protocol/ro/ProtocolRoService.getCCServicePriceEnquiry(
ProtocolRoService.java:368(Compiled Code))
4XESTACKTRACE at sun/reflect/GeneratedMethodAccessor26.invoke(Bytecode PC:40(Compiled
Code))
4XESTACKTRACE at sun/reflect/DelegatingMethodAccessorImpl.invoke
(DelegatingMethodAccessorImpl.java:43(Compiled Code))
4XESTACKTRACE at java/lang/reflect/Method.invoke(Method.java:615(Compiled Code))
|
清单 6
3XMTHREADINFO "WebContainer : 61" (TID:0x33045F00, sys_thread_t:0x32BB9B28, state:B,
native ID:0x0001399B) prio=5
4XESTACKTRACE at com/ibm/protocol/base/ProtocolBaseApiHelper.sendProtocolPacket
(ProtocolBaseApiHelper.java:1196(Compiled Code))
4XESTACKTRACE at com/ibm/protocol/base/ProtocolBaseApiImpl.sendProtocolPacket
(ProtocolBaseApiImpl.java:649(Compiled Code))
4XESTACKTRACE at com/ibm/protocol/ro/ProtocolInterface.transmitPacket
(ProtocolInterface.java:68(Compiled Code))
4XESTACKTRACE at com/ibm/protocol/ro/CreditControlRequest.transmitPacket
(CreditControlRequest.java:390(Compiled Code))
4XESTACKTRACE at com/ibm/protocol/ro/CreditControlRequest.sendChargingInfo
(CreditControlRequest.java:147(Compiled Code))
4XESTACKTRACE at com/ibm/protocol/ro/ProtocolRoService.sendCCR
(ProtocolRoService.java:143(Compiled Code))
4XESTACKTRACE at com/ibm/protocol/ro/ProtocolRoService.sendCCRefund(ProtocolRoService.
java:329(Compiled Code))
4XESTACKTRACE at sun/reflect/GeneratedMethodAccessor33.invoke(Bytecode PC:40(Compiled
Code))
4XESTACKTRACE at sun/reflect/DelegatingMethodAccessorImpl.invoke
(DelegatingMethodAccessorImpl.java:43(Compiled Code))
4XESTACKTRACE at java/lang/reflect/Method.invoke(Method.java:615(Compiled Code))
|
WorkManager 线程(清单 7)有一些不同。在协议框架中,WorkManager 线程用于执行异步 Web 服务和数据库请求,以响应从协议服务器接收到的请求。WorkManager 线程还必需使用确认信息(表明已经收到了请求)对协议服务器进行响应。所有这些线程也会在尝试发送数据包时停止。其阻塞所在的代码行为:
cfsm.send(packet, null, packetCallback);
您发现,被阻塞的是将确认信息数据包发送回协议服务器的操作。另外,注意在这种情况下,有 50 个 WorkManager 线程受到了影响。在此示例中,WebSphere Application Server 配置为使用包括 50 个 WorkManager 线程的线程池。
清单 7
3XMTHREADINFO "WorkManager.ProtocolWorkManager : 0" (TID:0x35895600, sys_thread_t:
0x357ECAD8, state:B, native ID:0x0000B187) prio=5
4XESTACKTRACE at com/ibm/protocol/base/ProtocolBaseApiHelper.sendProtocolPacket
(ProtocolBaseApiHelper.java:1239(Compiled Code))
4XESTACKTRACE at com/ibm/protocol/base/ProtocolBaseApiImpl.sendProtocolPacket
(ProtocolBaseApiImpl.java:686(Compiled Code))
4XESTACKTRACE at com/ibm/protocol/ro/ProtocolInterface.transmitPacket(ProtocolInterface.
java:122(Compiled Code))
4XESTACKTRACE at com/ibm/protocol/ro/ReAuthRequest.createAndSendAnswerPacket
(ReAuthRequest.java:391(Compiled Code))
4XESTACKTRACE at com/ibm/protocol/ro/ReAuthRequest.run(ReAuthRequest.java:197(Compiled
Code))
4XESTACKTRACE at com/ibm/ws/asynchbeans/J2EEContext.run(J2EEContext.java:1114(Compiled
Code))
4XESTACKTRACE at com/ibm/ws/asynchbeans/WorkWithExecutionContextImpl.go
(WorkWithExecutionContextImpl.java:195(Compiled Code))
4XESTACKTRACE at com/ibm/ws/asynchbeans/CJWorkItemImpl.run(CJWorkItemImpl.java:150
(Compiled Code))
4XESTACKTRACE at com/ibm/ws/util/ThreadPool$Worker.run(ThreadPool.java:1469(Compiled
Code))
|
不可延期的 Alarm:0 线程也以稍微不同的方式阻塞。此线程由管理员尝试关闭服务器时产生。您可以看到此线程将要关闭应用程序。shutdown 进程尝试通过将所有连接关闭来将其删除。这将产生对 ProtocolConnection.stopConnection 的调用。如果查看堆栈跟踪对应的代码行,将会找到此代码:
this.getProtocolCfsmInitiator().stop();
这是对同一个 CFSM 的另一个同步调用,因此 shutdown 线程也会受到死锁的影响。
清单 8
3XMTHREADINFO "Non-deferrable Alarm : 0" (TID:0x33846900, sys_thread_t:0x3380B550, state:
B, native ID:0x00017469) prio=5
4XESTACKTRACE at com/ibm/protocol/cfsm/ProtocolConnection.stopConnection
(ProtocolConnection.java:520)
4XESTACKTRACE at com/ibm/protocol/base/ProtocolBaseApiHelper.confRemovePeer
(ProtocolBaseApiHelper.java:719)
4XESTACKTRACE at com/ibm/protocol/base/ProtocolBaseApiHelper.unRegisterApplicationId
(ProtocolBaseApiHelper.java:339)
4XESTACKTRACE at com/ibm/protocol/base/ProtocolBaseApiImpl.unRegisterApplicationId
(ProtocolBaseApiImpl.java:448)
4XESTACKTRACE at com/ibm/protocol/util/DiamInitBaseServlet.unRegister(DiamInitBaseServlet.
java:1037)
4XESTACKTRACE at com/ibm/protocol/util/DiamInitBaseServlet.destroy(DiamInitBaseServlet.
java:1059)
4XESTACKTRACE at com/ibm/protocol/servlet/ProtocolInitServlet.destroy(ProtocolInitServlet.
java:215)
4XESTACKTRACE at com/ibm/ws/webcontainer/servlet/ServletWrapper.doDestroy(ServletWrapper.
java:801)
4XESTACKTRACE at com/ibm/ws/wswebcontainer/servlet/ServletWrapper.doDestroy
(ServletWrapper.java:677)
4XESTACKTRACE at com/ibm/ws/webcontainer/servlet/ServletWrapper.destroy(ServletWrapper.
java:880)
4XESTACKTRACE at com/ibm/ws/webcontainer/webapp/WebApp.destroy(WebApp.java:2594)
4XESTACKTRACE at com/ibm/ws/wswebcontainer/webapp/WebApp.destroy(WebApp.java:1078)
4XESTACKTRACE at com/ibm/ws/container/AbstractContainer.destroy(AbstractContainer.java:82)
4XESTACKTRACE at com/ibm/ws/webcontainer/webapp/WebGroup.destroy(WebGroup.java:194)
4XESTACKTRACE at com/ibm/ws/webcontainer/webapp/WebGroup.removeWebApplication(WebGroup.
java:232)
4XESTACKTRACE at com/ibm/ws/webcontainer/VirtualHost.removeWebApplication(VirtualHost.
java:282)
4XESTACKTRACE at com/ibm/ws/wswebcontainer/VirtualHost.removeWebApplication(VirtualHost.
java:181)
4XESTACKTRACE at com/ibm/ws/wswebcontainer/WebContainer.removeWebApplication(WebContainer.
java:735)
4XESTACKTRACE at com/ibm/ws/webcontainer/component/WebContainerImpl.uninstall
(WebContainerImpl.java:359)
4XESTACKTRACE at com/ibm/ws/webcontainer/component/WebContainerImpl.stop(WebContainerImpl.
java:562)
4XESTACKTRACE at com/ibm/ws/runtime/component/ApplicationMgrImpl.stop(ApplicationMgrImpl.
java:1324)
4XESTACKTRACE at com/ibm/ws/runtime/component/DeployedApplicationImpl.
fireDeployedObjectStop(DeployedApplicationImpl.java:1143)
4XESTACKTRACE at com/ibm/ws/runtime/component/DeployedModuleImpl.stop(DeployedModuleImpl.
java:602)
|
最后,ProtocolThreadPool: 0、ProtocolThreadPool: 1 及 ProtocolThreadPool: 2 和 ProtocolThreadPool:0 及 ProtocolThreadPool:2 都在 ProtocolChannelReader 中等待协议通道将协议数据包传递过来进行处理,因此它们都完全处于空闲状态。ThreadPool:0 和 ThreadPool:2 都不是造成问题的原因,这个问题是由于 ThreadPool:1 造成的。
清单 9
3XMTHREADINFO "ProtocolThreadPool : 0" (TID:0x345FD800, sys_thread_t:0x345E3FA8, state:CW,
native ID:0x0000F6B3) prio=5
4XESTACKTRACE at java/lang/Object.wait(Native Method)
4XESTACKTRACE at java/lang/Object.wait(Object.java:231(Compiled Code))
4XESTACKTRACE at com/ibm/protocol/cfsm/ProtocolChannelReader.run(ProtocolChannelReader.
java:253)
4XESTACKTRACE at com/ibm/ws/util/ThreadPool$Worker.run(ThreadPool.java:1469)
|
清单 10
3XMTHREADINFO "ProtocolThreadPool : 2" (TID:0x3464C000, sys_thread_t:0x345E44D8, state:CW,
native ID:0x0000E419) prio=5
4XESTACKTRACE at java/lang/Object.wait(Native Method)
4XESTACKTRACE at java/lang/Object.wait(Object.java:231(Compiled Code))
4XESTACKTRACE at com/ibm/protocol/cfsm/ProtocolChannelReader.run(ProtocolChannelReader
.java:253)
4XESTACKTRACE at com/ibm/ws/util/ThreadPool$Worker.run(ThreadPool.java:1469)
|
清单 11 显示了 ProtocolThreadPool:1,该线程正在进行大量的工作:它收到了一个数据包,正在尝试将该数据包提交到更高的层进行处理。最后,它尝试将数据包转交给 WorkManager 线程,而这正是阻塞调用 Object.wait() 方法的地方。enqueue 操作被阻塞,等待可用线程。这样就形成了死锁。
清单 11
3XMTHREADINFO "ProtocolThreadPool : 1" (TID:0x3464BC00, sys_thread_t:0x345E4240, state:CW,
native ID:0x0000E6EF) prio=5
4XESTACKTRACE at java/lang/Object.wait(Native Method)
4XESTACKTRACE at java/lang/Object.wait(Object.java:231(Compiled Code))
4XESTACKTRACE at com/ibm/ws/util/BoundedBuffer.waitPut_(BoundedBuffer.java:211(Compiled
Code))
4XESTACKTRACE at com/ibm/ws/util/BoundedBuffer.put(BoundedBuffer.java:323(Compiled Code))
4XESTACKTRACE at com/ibm/ws/util/ThreadPool.execute(ThreadPool.java:1135(Compiled Code))
4XESTACKTRACE at com/ibm/ws/util/ThreadPool.execute(ThreadPool.java:1014(Compiled Code))
4XESTACKTRACE at com/ibm/ws/asynchbeans/WorkItemImpl$PoolExecuteProxy.run(WorkItemImpl.
java:197(Compiled Code))
4XESTACKTRACE at com/ibm/ws/asynchbeans/WorkItemImpl.executeOnPool(WorkItemImpl.java:211
(Compiled Code))
4XESTACKTRACE at com/ibm/ws/asynchbeans/WorkManagerImpl.queueWorkItemForDispatch
(WorkManagerImpl.java:400(Compiled Code))
4XESTACKTRACE at com/ibm/ws/asynchbeans/WorkManagerImpl.schedule(WorkManagerImpl.java:951
(Compiled Code))
4XESTACKTRACE at com/ibm/ws/asynchbeans/WorkManagerImpl.schedule(WorkManagerImpl.java:771
(Compiled Code))
4XESTACKTRACE at com/ibm/protocol/servlet/ProtocolInitServlet.packetEvent
(ProtocolInitServlet.java:287(Compiled Code))
4XESTACKTRACE at com/ibm/protocol/cfsm/ProtocolState_RI_Open.handleRequestDataPacket
(ProtocolState_RI_Open.java:611(Compiled Code))
4XESTACKTRACE at com/ibm/protocol/cfsm/ProtocolState_RI_Open.packetReceived
(ProtocolState_RI_Open.java:176(Compiled Code))
4XESTACKTRACE at com/ibm/protocol/cfsm/ProtocolCfsm.packetReceived(ProtocolCfsm.java:95
(Compiled Code))
4XESTACKTRACE at com/ibm/protocol/cfsm/ProtocolChannelReader.run(ProtocolChannelReader.
java:204)
4XESTACKTRACE at com/ibm/ws/util/ThreadPool$Worker.run(ThreadPool.java:1469)
|
ProtocolThreadPool:1 线程(持有 CFSM 锁定)等待 WorkManager 线程变为可用。WorkManager 线程全部都在使用,等待获得对 CFSM 的锁定和传输数据包。没有任何线程能够继续工作。图 1 显示了这种情况:
- 36 个 WebContainer 线程在等待获得对 CFSM 监视器的访问来传输数据包。
- 50 个 WorkManager 线程也在等待获得对 CFSM 监视器的访问来传输数据包。
- Reader 线程是具有对 CFSM 监视器的访问的线程,接收了一个入站数据包,并希望将该数据包传递给 WorkManager 线程进行处理。
- 由于所有 WorkManager 线程都在使用,Reader 线程将阻塞,等待释放资源——但由于都在等待使用 CFSM 传输数据包,因此永远不会被释放。
图 1. 死锁系统
这个特定的死锁解决起来非常容易,您可以在没有 WorkManager 线程可用时直接更改调用 WorkManagemer 线程的方法来抛出一个异常,而不是阻塞。这样可通过丢弃数据包并记录错误消息(而不是锁定)来打破死锁循环。ProtocolThreadPool:1 线程可以返回和释放 CFSM 锁定,然后 WorkManager 和 WebContainer 线程就可以传输数据包。
结束语
为了说明如何跟踪典型的死锁问题,本文使用了锁定和线程堆栈跟踪来查找导致很多线程被锁定的单个资源 (CFSM)。找到了持有资源的线程 (ProtocolThreadPool:1)及其等待的资源(WorkManager 线程可用性),从而确定了导致死锁的完整循环。要解决死锁问题,需要找到释放资源的方法(丢弃数据包并释放 CFSM),避免再次出现循环依赖关系。希望通过本文,您将能够应用这个基本示例中描述的原则和方法,以避免和解决自己应用程序中的死锁情况。
http://www.ibm.com/developerworks/cn/websphere/library/techarticles/0805_smith/0805_smith.html?S_TACT=105AGX52&S_CMP=tec-ccid#N100BC