1

什么是要做?

  1. 从这里下载 logica smpp jar (215 KB):http: //opensmpp.logica.com/CommonPart/Download/library_1_3/smpp_full.tar.gz
  2. 写一个小测试代码:

    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();
        }
    }
    
  3. 运行此main方法。

怎么了?

s 被阻塞(大概是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以便它可以通过它提供的工厂方法创建其子对象的对象,例如BindTransmitter,DataSMOutbindcreatePDU

  • 因此,当我的测试应用程序被执行时,ONEThread进入 PDU 的静态方法(在初始化时DataSM)。Thread而已经开始初始化的 TWOOutbind等待 ONE 完成初始化PDU

  • 但是在运行静态方法的 ONE 中的某个时刻PDU,它尝试初始化Outbind,并且看到 TWO 已经开始了相同的事情,它等待 TWO 完成。

  • 所以一和二在等待对方完成

  • 我怎么能确信这个问题与静态块加载有关?
    在测试代​​码的 main 方法中添加以下一行作为第一条语句,使其工作并且Threads 不再阻塞:

    Class.forName("com.logica.smpp.pdu.PDU");
    

我的问题是:

  1. 我的分析正确吗?
  2. 这是Thread与静态块有关的已知同步问题吗?
  3. 有没有什么经验法则需要练习才能避免遇到这种情况?

更新

  • 在此处添加 PDU 的工厂方法:

    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;
    }
    
  • 的构造函数DataSMOutbind其他子类PDU做什么?
    什么都没有,除了初始化一些实例变量。这些是 POJO。他们不保留任何外部资源,如文件、数据库等。

4

4 回答 4

1

您的线程可能会阻塞,但不是出于您认为的原因。静态初始化器在类加载时执行,而不是在创建实例时执行。所以,你没有让你的两个对象“进入”静态初始化程序,并且在某些时候,在某个共享变量上陷入僵局。

在不知道您正在使用的库的确切细节的情况下,很难诊断出真正的问题可能是什么,但我建议您转储您的线程并使用合适的工具对其进行分析。

于 2013-02-24T05:35:18.140 回答
1

如果一个类的初始化器依赖于另一个类,反之亦然,如果两个类在 2 个不同的线程中初始化,就会导致死锁。您的分析很可能是正确的。

如果这两个类在同一个线程中初始化,就不会出现死锁;然而,应该避免循环依赖。在这个例子中,这些pduList东西应该在它自己的类中。

于 2013-02-24T20:23:59.587 回答
0

请添加lock(pduList)为 FIRST 语句createPDU。在 . 之后立即关闭lockreturn。请尝试一下。让我知道你是怎么做的。

例如

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  
}
于 2013-02-25T00:22:14.180 回答
0

您可以使用该jstack工具打印出线程堆栈:

jstack <pid of your java process>

你可以通过jps命令找到pid。比如jps我机器的命令输出:

eric@erics-MacBook-Pro:~$ jps
577 Jps
448 

然后是448的jstack命令: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)

您可以看到“Worker-46”被阻止在Object.wait().

当您获得线程堆栈时,应该清楚为什么代码阻塞。

于 2013-02-24T05:40:53.553 回答