Java - 静态块和线程同步问题 - 手头的案例

时间:2013-02-24 05:26:26

标签: java multithreading smpp

该怎么办?

  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方法。

  4. 会发生什么?

    Thread被阻止(大概是在等待彼此)

    我的分析:

    • DataSMOutbind类都有一个共同的祖先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}处提供的工厂方法创建其子项的对象,如BindTransmitterDataSMOutbind等}

    • 因此,当我的测试应用程序执行时,ONE createPDU进入PDU的静态方法(初始化Thread时)。已经开始初始化DataSM的两个Thread等待一个人完成初始化Outbind

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

    • 所以ONE和TWO正在等待彼此完成

    • 我如何确信此问题与静态块加载有关? 添加以下一行作为测试代码主方法中的第一个语句,使其工作,Outbind不再阻塞:

      Thread

    我的问题是这些:

    1. 我的分析是否正确?
    2. 这是与静态块有关的已知Class.forName("com.logica.smpp.pdu.PDU"); 同步问题吗?
    3. 任何需要练习的拇指规则都不会发现这种情况?
    4. 更新

      • 在此处添加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。它们不会保留任何外部资源,如文件,数据库等。

4 个答案:

答案 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  
}