在 WebSphere Application Server V6.1 應用程式中跟蹤死鎖

CloudSpace發表於2009-11-24

引言

當兩個或多個執行緒彼此形成迴圈依賴關係時,就出現了死鎖。例如,如果執行緒 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: 
3LKNOTIFYQ            Waiting to be notified:
3LKWAITNOTIFY            "ProtocolThreadPool: 0" (0x345FD800)
2LKMONINUSE      sys_mon_t:0x346189D8 infl_mon_t: 0x34618A00:
3LKMONOBJECT       java/lang/Object@A05AE930/A05AE93C: 
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)
                         <... more="" of="" these="" ...="">

3LKWAITER                "Non-deferrable Alarm : 0" (0x33846900)

3LKWAITER                "WorkManager.ProtocolWorkManager : 0" (0x35895600)
3LKWAITER                "WorkManager.ProtocolWorkManager : 1" (0x3580EB00)
                         <... more="" of="" these="" ...="">

2LKMONINUSE      sys_mon_t:0x35815D30 infl_mon_t: 0x35815D58:
3LKMONOBJECT       java/lang/Object@A0A3CF90/A0A3CF9C: 
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. 死鎖系統
圖 1. 死鎖系統

這個特定的死鎖解決起來非常容易,您可以在沒有 WorkManager 執行緒可用時直接更改呼叫 WorkManagemer 執行緒的方法來丟擲一個異常,而不是阻塞。這樣可通過丟棄資料包並記錄錯誤訊息(而不是鎖定)來打破死鎖迴圈。ProtocolThreadPool:1 執行緒可以返回和釋放 CFSM 鎖定,然後 WorkManager 和 WebContainer 執行緒就可以傳輸資料包。







結束語

為了說明如何跟蹤典型的死鎖問題,本文使用了鎖定和執行緒堆疊跟蹤來查詢導致很多執行緒被鎖定的單個資源 (CFSM)。找到了持有資源的執行緒 (ProtocolThreadPool:1)及其等待的資源(WorkManager 執行緒可用性),從而確定了導致死鎖的完整迴圈。要解決死鎖問題,需要找到釋放資源的方法(丟棄資料包並釋放 CFSM),避免再次出現迴圈依賴關係。希望通過本文,您將能夠應用這個基本示例中描述的原則和方法,以避免和解決自己應用程式中的死鎖情況。

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/14789789/viewspace-480102/,如需轉載,請註明出處,否則將追究法律責任。

相關文章