Java-静态块和线程同步问题-一个案例 该怎么办? 从以下位置下载logica smpp jar(215 KB):

Java-静态块和线程同步问题-一个案例 该怎么办? 从以下位置下载logica smpp jar(215 KB):,java,multithreading,smpp,Java,Multithreading,Smpp,编写一个小测试代码: 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 Runn

编写一个小测试代码:

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

  • 会发生什么?
    线程
    被阻塞(可能正在等待对方)

    我的分析是:
    • DataSM
      Outbind
      类都有一个共同的祖先
      PDU
      ,它有一个带有以下代码的
      静态
      块:

      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
    ,以便通过它在
    createPDU
    提供的工厂方法创建其子对象,如
    BindTransmitter
    DataSM
    Outbind

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

    • 但是,在一个运行静态方法
      PDU
      的程序中,它试图初始化
      Outbind
      ,看到两个已经开始了相同的事情,它就等待两个完成

    • 所以一个和两个都在等待对方完成

    • 我如何才能确信此问题与静态块加载有关?
      在测试代码的主要方法中添加以下一行作为第一条语句,可以使其正常工作,
      线程不再阻塞:

      Class.forName("com.logica.smpp.pdu.PDU");
      
    我的问题是:
  • 我的分析正确吗
  • 这是与静态块有关的已知
    线程
    同步问题吗
  • 有没有什么需要练习的拇指法则可以避免在这种情况下绊倒
  • 更新
    • 在此处添加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;
      }
      
      公共静态最终PDU createPDU(int commandId)
      {
      int size=pduList.size();
      PDU PDU=null;
      PDU newInstance=null;
      对于(int i=0;i
    • DataSM
      Outbind
      PDU
      的其他子类的构造函数做什么?
      什么都没有,只是初始化了几个实例变量。这些是波乔。他们不保留任何外部资源,如文件、数据库等


      • 您的线程可能会阻塞,但不是因为您认为的原因。静态初始值设定项在加载类时执行,而不是在创建实例时执行。因此,您不会让您的两个对象“进入”静态初始值设定项,在某个点上,在某个共享变量上陷入死锁

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


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

        jstack <pid of your java process>
        
        然后使用
        pid
        448执行
        jstack
        命令:

        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”prio=5 tid=10f543800 nid=0x11811e000在Object.wait()中[11811d000]
        java.lang.Thread.State:定时等待(在对象监视器上)
        在java.lang.Object.wait(本机方法)
        -等待(org.eclipse.core.internal.jobs.WorkerPool)
        位于org.eclipse.core.internal.jobs.WorkerPool.sleep(WorkerPool.java:188)
        -锁定(org.eclipse.core.internal.jobs.WorkerPool)
        位于org.eclipse.core.internal.jobs.WorkerPool.startJob(WorkerPool.java:220)
        位于org.eclipse.core.internal.jobs.Worker.run(Worker.java:50)
        
        您可以在
        对象中看到“Worker-46”被阻塞。wait()


        获取线程堆栈时,应该清楚代码阻塞的原因。

        如果一个类的初始值设定项依赖于另一个类,反之亦然,则如果两个类在两个不同的线程中初始化,则可能导致死锁。你的分析很可能是正确的

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

        请将
        lock(pduList)
        添加为
        createPDU
        中的第一条语句。在
        返回后立即关闭
        块。请试一试。让我知道你是怎么做的

        e、 g

        公共静态最终PDU createPDU(int commandId)
        {
        锁(pduList){
        int size=pduList.size();
        PDU PDU=null;
        PDU newInstance=null;
        对于(int i=0;i
        但是正如您从我的测试应用程序中注意到的,当
        线程开始执行时,
        DataSM
        Outbind
        这两个类都是第一次加载的。因此,它们的祖先
        PDU
        的静态块被输入到这些
        线程的范围中。此外,此库的源代码也可用。这些对象是POJO。它们根本不保留任何外部资源。除非您的类被加载到不同的类加载器中,否则情况就不会如此。斯达
        
        "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)
        
        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  
        }