写一个小测试代码:
package com.logica.smpp;
import com.logica.smpp.pdu.DataSM;
import com.logica.smpp.pdu.Outbind;
public class PDUTest {
public static void main(String... args) throws InterruptedException {
Thread thread1 = new Thread(new Runnable() {
@Override
public void run() {
System.out.println(new DataSM().debugString());
}
});
thread1.setName("ONE");
Thread thread2 = new Thread(new Runnable() {
@Override
public void run() {
System.out.println(new Outbind().debugString());
}
});
thread2.setName("TWO");
thread1.start();
thread2.start();
}
}
运行此main
方法。
Thread
被阻止(大概是在等待彼此)
DataSM
和Outbind
类都有一个共同的祖先PDU
,它有一个static
块,代码如下:
static {
pduList = new Vector(30,4);
pduList.add(new BindTransmitter());
pduList.add(new BindTransmitterResp());
pduList.add(new BindReceiver());
pduList.add(new BindReceiverResp());
pduList.add(new BindTransciever());
pduList.add(new BindTranscieverResp());
pduList.add(new Unbind());
pduList.add(new UnbindResp());
pduList.add(new Outbind());
pduList.add(new SubmitSM());
pduList.add(new SubmitSMResp());
pduList.add(new SubmitMultiSM());
pduList.add(new SubmitMultiSMResp());
pduList.add(new DeliverSM());
pduList.add(new DeliverSMResp());
pduList.add(new DataSM());
pduList.add(new DataSMResp());
pduList.add(new QuerySM());
pduList.add(new QuerySMResp());
pduList.add(new CancelSM());
pduList.add(new CancelSMResp());
pduList.add(new ReplaceSM());
pduList.add(new ReplaceSMResp());
pduList.add(new EnquireLink());
pduList.add(new EnquireLinkResp());
pduList.add(new AlertNotification());
pduList.add(new GenericNack());
}
它创建pduList
,以便它可以通过{{1}处提供的工厂方法创建其子项的对象,如BindTransmitter
,DataSM
,Outbind
等}
因此,当我的测试应用程序执行时,ONE createPDU
进入PDU的静态方法(初始化Thread
时)。已经开始初始化DataSM
的两个Thread
等待一个人完成初始化Outbind
。
但是在运行静态方法PDU
的ONE中的某个时刻,它尝试初始化PDU
,并且看到TWO已经开始相同的事情,它等待TWO到光洁度。
所以ONE和TWO正在等待彼此完成
我如何确信此问题与静态块加载有关?
添加以下一行作为测试代码主方法中的第一个语句,使其工作,Outbind
不再阻塞:
Thread
Class.forName("com.logica.smpp.pdu.PDU");
同步问题吗?在此处添加PDU的工厂方法:
Thread
public static final PDU createPDU(int commandId)
{
int size = pduList.size();
PDU pdu = null;
PDU newInstance = null;
for (int i = 0; i < size; i++) {
pdu = (PDU)pduList.get(i);
if (pdu != null) {
if (pdu.getCommandId() == commandId) {
try {
newInstance = (PDU)(pdu.getClass().newInstance());
} catch (IllegalAccessException e) {
} catch (InstantiationException e) {
}
return newInstance;
}
}
}
return null;
}
,DataSM
的构造函数和Outbind
的其他子类做了什么?
没什么,除了初始化一些实例变量。这些是POJO。它们不会保留任何外部资源,如文件,数据库等。
答案 0 :(得分:1)
您的主题可能会受阻,但不是您认为的原因。静态初始值设定项在加载类时执行,而不是在创建实例时执行。所以,你没有让你的两个对象“输入”静态初始化器,并且在某些时候,在一些共享变量上变得死锁。
在不知道您正在使用的库的确切细节的情况下,很难诊断出真正的问题,但我建议您使用一个不错的工具来转储线程并进行分析。
答案 1 :(得分:1)
如果一个类的初始化程序依赖于另一个类,反之亦然,如果在两个不同的线程中初始化两个类,则会导致死锁。你的分析很可能是正确的。
如果在同一个线程中初始化这两个类,则不会出现死锁;尽管如此,应该避免循环依赖。在这个例子中,pduList
内容应该在它自己的类中。
答案 2 :(得分:0)
您可以使用jstack
工具打印出线程堆栈:
jstack <pid of your java process>
您可以通过jps
命令找到pid。例如,我的机器的jps
命令输出:
eric@erics-MacBook-Pro:~$ jps
577 Jps
448
然后使用jstack
448的pid
命令:
jstack 448
其部分输出:
"Worker-46" prio=5 tid=10f543800 nid=0x11811e000 in Object.wait() [11811d000]
java.lang.Thread.State: TIMED_WAITING (on object monitor)
at java.lang.Object.wait(Native Method)
- waiting on <7a0001a80> (a org.eclipse.core.internal.jobs.WorkerPool)
at org.eclipse.core.internal.jobs.WorkerPool.sleep(WorkerPool.java:188)
- locked <7a0001a80> (a org.eclipse.core.internal.jobs.WorkerPool)
at org.eclipse.core.internal.jobs.WorkerPool.startJob(WorkerPool.java:220)
at org.eclipse.core.internal.jobs.Worker.run(Worker.java:50)
您可以在Object.wait()
中看到“工人-46”被屏蔽。
应该清楚为什么代码在你获得线程堆栈时会阻塞。
答案 3 :(得分:0)
请在lock(pduList)
中添加createPDU
作为FIRST声明。在lock
之后立即关闭return
块。试试吧。让我知道你如何使用它。
e.g。
public static final PDU createPDU(int commandId)
{
lock(pduList) {
int size = pduList.size();
PDU pdu = null;
PDU newInstance = null;
for (int i = 0; i < size; i++) {
pdu = (PDU)pduList.get(i);
if (pdu != null) {
if (pdu.getCommandId() == commandId) {
try {
newInstance = (PDU)(pdu.getClass().newInstance());
} catch (IllegalAccessException e) {
} catch (InstantiationException e) {
}
return newInstance;
}
}
}
return null;
} // end lock
}