描述
线程暂时阻塞并不是什么问题。不过,如果其长时间阻塞,这就是值得注意的问题了——因为其表明程序中存在一些问题。通常阻塞的线程会导致程序无响应。
在 10 秒间隔内捕获的 3 个线程转储文件中,如果线程都在相同的方法上并处于‘BLOCKED’状态,就说明可能存在问题。研究此类阻塞线程的栈追踪信息将说明其阻塞原因。可能的理由有:死锁、循环死锁、另一线程、获取了锁定且未将其释放、外部 SOR 可能无响应等等…
示例
以下是摘自大型 SOA 应用程序线程转储文件的部分内容。该程序在运行时进入了无响应状态。线程 ajp-bio-192.168.100.128-9022-exec-173 在 3 个连续的线程转储文件中(捕获间隔:10 秒)都保持着 BLOCKED 状态。以下是此线程栈追踪信息中的重要部分:
ajp-bio-192.168.100.128-9022-exec-173
Stack Trace is:
java.lang.Thread.State: BLOCKED (on object monitor)
at **.***.sp.dao.impl.ReferenceNumberGeneratorDaoImpl.getNextItineryReferenceNumber(ReferenceNumberGeneratorDaoImpl.java:55)
- waiting to lock 0x00000006afaa5a60 (a **.***.sp.dao.impl.ReferenceNumberGeneratorDaoImpl)
at sun.reflect.GeneratedMethodAccessor3112.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:601)
at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:307)
:
:
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
at java.lang.Thread.run(Thread.java:722)
这里您可主要到 ajp-bio-192.168.100.128-9022-exec-173 卡在了方法 .*.sp.dao.impl.ReferenceNumberGeneratorDaoImpl.getNextItineryReferenceNumber(ReferenceNumberGeneratorDaoImpl.java:55) 上。此线程在该方法上停留时间过久,因为另一个线程 ajp-bio-192.168.100.128-9022-exec-84 在获取了锁 0x00000006afaa5a60 后未将其返回。下面是 ajp-bio-192.168.100.128-9022-exec-84 线程的栈追踪信息
ajp-bio-192.168.100.128-9022-exec-84
Stack Trace is:
java.lang.Thread.State: RUNNABLE
at java.net.SocketInputStream.socketRead0(Native Method)
at java.net.SocketInputStream.read(SocketInputStream.java:150)
at java.net.SocketInputStream.read(SocketInputStream.java:121)
at java.io.DataInputStream.readFully(DataInputStream.java:195)
at java.io.DataInputStream.readFully(DataInputStream.java:169)
at net.sourceforge.jtds.jdbc.SharedSocket.readPacket(SharedSocket.java:850)
at net.sourceforge.jtds.jdbc.SharedSocket.getNetPacket(SharedSocket.java:731)
- locked 0x00000006afa88a68 (a java.util.concurrent.ConcurrentHashMap)
at net.sourceforge.jtds.jdbc.ResponseStream.getPacket(ResponseStream.java:477)
at net.sourceforge.jtds.jdbc.ResponseStream.read(ResponseStream.java:114)
at net.sourceforge.jtds.jdbc.ResponseStream.peek(ResponseStream.java:99)
at net.sourceforge.jtds.jdbc.TdsCore.wait(TdsCore.java:4127)
at net.sourceforge.jtds.jdbc.TdsCore.executeSQL(TdsCore.java:1086)
- locked 0x00000006bca709f8 (a net.sourceforge.jtds.jdbc.TdsCore)
at net.sourceforge.jtds.jdbc.JtdsStatement.executeSQLQuery(JtdsStatement.java:493)
at net.sourceforge.jtds.jdbc.JtdsPreparedStatement.executeQuery(JtdsPreparedStatement.java:1032)
at com.jolbox.bonecp.PreparedStatementHandle.executeQuery(PreparedStatementHandle.java:174)
at **.***.sp.dao.impl.ReferenceNumberGeneratorDaoImpl.getNextItineryReferenceNumber(ReferenceNumberGeneratorDaoImpl.java:65)
- locked 0x00000006afaa5a60 (a **.***.sp.dao.impl.ReferenceNumberGeneratorDaoImpl)
at sun.reflect.GeneratedMethodAccessor3112.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:601)
at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:307)
:
:
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
at java.lang.Thread.run(Thread.java:722)
显然,这里没有设置 JDBC 读取超时时间,因此 ajp-bio-192.168.100.128-9022-exec-84 未能从 JDBC 调用中返回。因此其阻塞了调用 **.***.sp.dao.impl.ReferenceNumberGeneratorDaoImpl.getNextItineryReferenceNumber() 方法的所有其他线程。
为什么要将其命名为动脉硬化?
“动脉硬化”是一种心脏疾病。医学上其定义如下:动脉的内壁通常是光滑的,血液能够很轻松地流过。脂肪或斑块可能在动脉壁内积聚。这些斑块会使动脉变窄,导致血液流动减少甚至完全停止,最终结果就是患者死亡。
同样,如果线程阻塞持续并发生在多个线程上,那么其将会使程序进入无响应状态。最终我们将不得不强行退出程序。
Leave a Reply