调用自身时 EJB Singleton WRITE 锁不被尊重

EJB Singleton WRITE lock not honored when calling self

我已将此 EJB 部署到 Glassfish 4 (Java EE 7):

import javax.ejb.EJB;
import javax.ejb.Lock;
import javax.ejb.LockType;
import javax.ejb.Schedule;
import javax.ejb.Singleton;

@Singleton
@Lock(LockType.WRITE) // WRITE is default, but here for emphasis
public class SingletonBean {

    @EJB
    SingletonBean self;

    @Schedule(second="*/3", minute="*", hour="*")
    public void test_automatic_timer() throws InterruptedException {
        System.out.println("test_automatic_timer()");
        self.test();
    }

    public void test() {
        System.out.println("test()");
    }

}

我预计这会陷入僵局。当定时器调用test_automatic_timer时,它获得了WRITE锁。对 self.test() 的调用应该永远等待锁被释放,但在日志中我有:

Info:   test_automatic_timer()
Info:   test()
Info:   test_automatic_timer()
Info:   test()

为什么这不像我预期的那样工作?

p.s。这不完全是学术性的。我想进行 self 调用以便获得 a new container-managed transaction,但我想确保我首先更好地理解同步。


确认史蒂夫的回答

史蒂夫的回答是正确的。为了确认这一点,我记录了线程 ID,发现它们是相同的。我也想确认锁是否起作用,所以我重写了方法体如下:

@Schedule(second="*/3", minute="*", hour="*")
public void test_automatic_timer() throws InterruptedException {
    System.out.println("test_automatic_timer()");
    System.out.println(Thread.currentThread());
    self.test();
    Thread.sleep(10_000);
}

@Asynchronous
public void test() {
    System.out.println("test()");
    System.out.println(Thread.currentThread());
}

如果这按预期工作,对 test 的异步调用将被阻塞十秒钟,直到 test_automatic_timer 完成。如果不行,test会立即执行。日志显示:

Info:   test_automatic_timer()
Info:   Thread[__ejb-thread-pool11,5,main]
// 10 second pause...
Info:   test()
Info:   Thread[__ejb-thread-pool12,5,main]

所以它有效。

这段代码没有死锁,因为对 test() 的调用是在同一个(计时器执行)线程中进行的。

也就是说,我认为至少在像这样自我注入单例时的某些行为可能是未定义的。如果引入引用 self@PostConstruct 方法会发生什么?

您的 bean 名称用词不当,因为 @Singleton bean 是有状态的。

无论如何,这样做可能更安全:

@Singleton
@Lock(LockType.WRITE) // WRITE is default, but here for emphasis
public class SingletonBean {

    @Resource
    private SessionContext sessionContext;

    @Schedule(second="*/3", minute="*", hour="*")
    public void test_automatic_timer() throws InterruptedException {
        System.out.println("test_automatic_timer()");
        sessionContext.getBusinessObject(StatelessSessionBean.class).test();
    }

    @Transactional(REQUIRES_NEW)
    public void test() {
        System.out.println("test()");
    }

}

这确实是使用新事务自调用 EJB 的方法。