两个单例的EJB相互调用死锁

Deadlock in EJB mutual invocation of two singletons

我有 2 个 EJB @Singletons,它们在 tomee运行

他们互相调用,陷入僵局

@Startup
@Singleton
@ConcurrencyManagement(ConcurrencyManagementType.CONTAINER)
public class DeadlockReproducer {
    private final Logger LOGGER = Logger.getInstance(LogCategory.OPENEJB_STARTUP_CONFIG, this.getClass());

    @EJB
    private DeadlockReproducerAid deadlockReproducerAid;

    @PostConstruct
    public  void reproduce(){
        LOGGER.info("reproduce");
        deadlockReproducerAid.lockMe();
    }

    public void youCantGetMe(){
        LOGGER.info("youCantGetMe");
    }
}


@Startup
@Singleton
@ConcurrencyManagement(ConcurrencyManagementType.CONTAINER)
public class DeadlockReproducerAid {
    private final Logger LOGGER = Logger.getInstance(LogCategory.OPENEJB_STARTUP_CONFIG, this.getClass());

    @EJB
    DeadlockReproducer deadlockReproducer;

    public void lockMe(){
        LOGGER.info("lockMe");
        deadlockReproducer.youCantGetMe();
    }
}

当 youCantGetMe 等待复制到 return 时,我遇到了死锁。

这是堆栈跟踪:

"localhost-startStop-1" #68 daemon prio=5 os_prio=0 tid=0x0000000056d1f000 nid=0x14e0 waiting on condition [0x0000000058aab000]
   java.lang.Thread.State: WAITING (parking)
    at sun.misc.Unsafe.park(Native Method)
    - parking to wait for  <0x00000000fc7a2cc8> (a java.util.concurrent.FutureTask)
    at java.util.concurrent.locks.LockSupport.park(LockSupport.java:175)
    at java.util.concurrent.FutureTask.awaitDone(FutureTask.java:429)
    at java.util.concurrent.FutureTask.get(FutureTask.java:191)
    at org.apache.openejb.core.singleton.SingletonInstanceManager.getInstance(SingletonInstanceManager.java:112)
    at org.apache.openejb.core.singleton.SingletonContainer.invoke(SingletonContainer.java:200)
    at org.apache.openejb.core.ivm.EjbObjectProxyHandler.synchronizedBusinessMethod(EjbObjectProxyHandler.java:308)
    at org.apache.openejb.core.ivm.EjbObjectProxyHandler.businessMethod(EjbObjectProxyHandler.java:303)
    at org.apache.openejb.core.ivm.EjbObjectProxyHandler._invoke(EjbObjectProxyHandler.java:92)
    at org.apache.openejb.core.ivm.BaseEjbProxyHandler.invoke(BaseEjbProxyHandler.java:308)
    at com.company.test.eu.logic.server.stateMachine.DeadlockReproducer$$LocalBeanProxy.youCantGetMe(com/company/test/eu/logic/server/stateMachine/DeadlockReproducer.java)
    at com.company.test.eu.logic.server.stateMachine.DeadlockReproducerAid.lockMe(DeadlockReproducerAid.java:31)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:483)
    at org.apache.openejb.core.interceptor.ReflectionInvocationContext$Invocation.invoke(ReflectionInvocationContext.java:192)
    at org.apache.openejb.core.interceptor.ReflectionInvocationContext.proceed(ReflectionInvocationContext.java:173)
    at org.apache.openejb.monitoring.StatsInterceptor.record(StatsInterceptor.java:181)
    at org.apache.openejb.monitoring.StatsInterceptor.invoke(StatsInterceptor.java:100)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:483)
    at org.apache.openejb.core.interceptor.ReflectionInvocationContext$Invocation.invoke(ReflectionInvocationContext.java:192)
    at org.apache.openejb.core.interceptor.ReflectionInvocationContext.proceed(ReflectionInvocationContext.java:173)
    at org.apache.openejb.core.interceptor.InterceptorStack.invoke(InterceptorStack.java:85)
    at org.apache.openejb.core.singleton.SingletonContainer._invoke(SingletonContainer.java:256)
    at org.apache.openejb.core.singleton.SingletonContainer.invoke(SingletonContainer.java:212)
    at org.apache.openejb.core.ivm.EjbObjectProxyHandler.synchronizedBusinessMethod(EjbObjectProxyHandler.java:308)
    at org.apache.openejb.core.ivm.EjbObjectProxyHandler.businessMethod(EjbObjectProxyHandler.java:303)
    at org.apache.openejb.core.ivm.EjbObjectProxyHandler._invoke(EjbObjectProxyHandler.java:92)
    at org.apache.openejb.core.ivm.BaseEjbProxyHandler.invoke(BaseEjbProxyHandler.java:308)
    at com.company.test.eu.logic.server.stateMachine.DeadlockReproducerAid$$LocalBeanProxy.lockMe(com/company/test/eu/logic/server/stateMachine/DeadlockReproducerAid.java)
    at com.company.test.eu.logic.server.stateMachine.DeadlockReproducer.reproduce(DeadlockReproducer.java:30)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:483)
    at org.apache.openejb.core.interceptor.ReflectionInvocationContext$Invocation.invoke(ReflectionInvocationContext.java:192)
    at org.apache.openejb.core.interceptor.ReflectionInvocationContext$LifecycleInvocation.invoke(ReflectionInvocationContext.java:223)
    at org.apache.openejb.core.interceptor.ReflectionInvocationContext.proceed(ReflectionInvocationContext.java:173)
    at org.apache.openejb.monitoring.StatsInterceptor.record(StatsInterceptor.java:181)
    at org.apache.openejb.monitoring.StatsInterceptor.PostConstruct(StatsInterceptor.java:109)
    at sun.reflect.GeneratedMethodAccessor37.invoke(Unknown Source)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:483)
    at org.apache.openejb.core.interceptor.ReflectionInvocationContext$Invocation.invoke(ReflectionInvocationContext.java:192)
    at org.apache.openejb.core.interceptor.ReflectionInvocationContext.proceed(ReflectionInvocationContext.java:173)
    at org.apache.openejb.core.interceptor.InterceptorStack.invoke(InterceptorStack.java:85)
    at org.apache.openejb.BeanContext.newInstance(BeanContext.java:1590)
    at org.apache.openejb.core.singleton.SingletonInstanceManager.createInstance(SingletonInstanceManager.java:179)
    at org.apache.openejb.core.singleton.SingletonInstanceManager.access0(SingletonInstanceManager.java:69)
    at org.apache.openejb.core.singleton.SingletonInstanceManager.call(SingletonInstanceManager.java:120)
    at org.apache.openejb.core.singleton.SingletonInstanceManager.call(SingletonInstanceManager.java:118)
    at java.util.concurrent.FutureTask.run(FutureTask.java:266)
    at org.apache.openejb.core.singleton.SingletonInstanceManager.getInstance(SingletonInstanceManager.java:129)
    at org.apache.openejb.core.singleton.SingletonInstanceManager.initialize(SingletonInstanceManager.java:93)
    at org.apache.openejb.core.singleton.SingletonInstanceManager.start(SingletonInstanceManager.java:84)
    at org.apache.openejb.core.singleton.SingletonContainer.start(SingletonContainer.java:125)
    at org.apache.openejb.assembler.classic.Assembler.startEjbs(Assembler.java:1168)
    at org.apache.openejb.assembler.classic.Assembler.createApplication(Assembler.java:807)
    at org.apache.openejb.assembler.classic.Assembler.createApplication(Assembler.java:623)
    at org.apache.tomee.catalina.TomcatWebAppBuilder.startInternal(TomcatWebAppBuilder.java:1248)
    at org.apache.tomee.catalina.TomcatWebAppBuilder.configureStart(TomcatWebAppBuilder.java:1087)
    at org.apache.tomee.catalina.GlobalListenerSupport.lifecycleEvent(GlobalListenerSupport.java:130)
    at org.apache.catalina.util.LifecycleSupport.fireLifecycleEvent(LifecycleSupport.java:117)
    at org.apache.catalina.util.LifecycleBase.fireLifecycleEvent(LifecycleBase.java:90)
    at org.apache.catalina.core.StandardContext.startInternal(StandardContext.java:5378)
    - locked <0x00000000e1115938> (a org.apache.catalina.core.StandardContext)
    at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:150)
    - locked <0x00000000e1115938> (a org.apache.catalina.core.StandardContext)
    at org.apache.catalina.core.ContainerBase.addChildInternal(ContainerBase.java:901)
    at org.apache.catalina.core.ContainerBase.addChild(ContainerBase.java:877)
    at org.apache.catalina.core.StandardHost.addChild(StandardHost.java:649)
    at org.apache.catalina.startup.HostConfig.deployWAR(HostConfig.java:1083)
    at org.apache.catalina.startup.HostConfig$DeployWar.run(HostConfig.java:1880)
    at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)
    at java.util.concurrent.FutureTask.run(FutureTask.java:266)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
    at java.lang.Thread.run(Thread.java:745)

   Locked ownable synchronizers:
    - <0x00000000fb344d08> (a java.util.concurrent.locks.ReentrantReadWriteLock$NonfairSync)
    - <0x00000000e0bca828> (a java.util.concurrent.ThreadPoolExecutor$Worker)

public void reproduce()@PostConstruct 注释调用时,它们会死锁。

当它被另一个 bean 调用时,youCantGetMe 执行 return。

例如,以下设置有效:

@Startup
@Singleton
@ConcurrencyManagement(ConcurrencyManagementType.CONTAINER)
public class DeadlockReproducer {
    private final Logger LOGGER = Logger.getInstance(LogCategory.OPENEJB_STARTUP_CONFIG, this.getClass());

    @EJB
    private DeadlockReproducerAid deadlockReproducerAid;

    //@PostConstruct - no postconstruct now
    public  void reproduce(){
        LOGGER.info("reproduce");
        deadlockReproducerAid.lockMe();
    }

    public void youCantGetMe(){
        LOGGER.info("youCantGetMe");
    }
}


@Startup
@Singleton
@ConcurrencyManagement(ConcurrencyManagementType.CONTAINER)
public class DeadlockReproducerAid {
    private final Logger LOGGER = Logger.getInstance(LogCategory.OPENEJB_STARTUP_CONFIG, this.getClass());

    @EJB
    DeadlockReproducer deadlockReproducer;

    public void lockMe(){
        LOGGER.info("lockMe");
        deadlockReproducer.youCantGetMe();
    }
}


@Startup
@Singleton
public class DeadlockInvoker {

    @EJB
    DeadlockReproducer deadlockReproducer;

    @PostConstruct
    public void startup(){
        deadlockReproducer.reproduce();
    }
}

为什么会这样,我怎样才能在不借助外部调用程序的情况下使它工作?

编辑:

我开始发现这种行为与 EJB 中的单例生命周期有关。

单例在 @PostConstruct 完成 运行 之前不会进入 Ready 状态,并且可能当它的状态不是 Ready 时,它不会响应外部方法调用。

您需要标记您的第一个 ejb 需要第二个在它之前启动:https://docs.oracle.com/javaee/6/api/javax/ejb/DependsOn.html

除此之外,我没有发现代码有任何问题,单例使用可重入锁,所以这应该可以工作。

我猜这个行为与 EJB 中的单例生命周期有关。

我用第三个启动 bean 解决了这个问题,它依赖于两个单例,并在两个单例都已加载时手动调用其中一个单例的 start() 方法。为此,我添加了 start() 方法。

我本可以从 DeadlockReproducerAid 开始 DeadlockReproducer,但这会掩盖第二个 class 的目的。