如果可能只有一个计时器同时处于活动状态,则有两种解决方案。
首先,
@Timer应该在
@Singleton。在Singleton中,方法默认情况下是写锁定的,因此,当容器中仍有活动时,尝试调用timer方法时,容器将自动锁定。
以下基本上就足够了:
@Singletonpublic class TimerBean { @Schedule(second= "*/5", minute = "*", hour = "*", persistent = false) public void atSchedule() throws InterruptedException { System.out.println("Called"); Thread.sleep(10000); }}atSchedule默认情况下是写锁定的,并且其中永远只有一个线程处于活动状态,包括由容器发起的调用。
锁定后,容器可能会重试计时器,因此,为防止这种情况,您可以改用读锁,然后委托给第二个bean(需要第二个bean,因为EJB
3.1不允许将读锁升级为写锁)。
计时器bean:
@Singletonpublic class TimerBean { @EJB private WorkerBean workerBean; @Lock(READ) @Schedule(second = "*/5", minute = "*", hour = "*", persistent = false) public void atSchedule() { try { workerBean.doTimerWork(); } catch (Exception e) { System.out.println("Timer still busy"); } }}工人豆:
@Singletonpublic class WorkerBean { @AccessTimeout(0) public void doTimerWork() throws InterruptedException { System.out.println("Timer work started"); Thread.sleep(12000); System.out.println("Timer work done"); }}这可能仍会在日志中显示一个嘈杂的异常,因此,更详细但更安静的解决方案是使用显式布尔值:
计时器bean:
@Singletonpublic class TimerBean { @EJB private WorkerBean workerBean; @Lock(READ) @Schedule(second = "*/5", minute = "*", hour = "*", persistent = false) public void atSchedule() { workerBean.doTimerWork(); }}工人豆:
@Singletonpublic class WorkerBean { private AtomicBoolean busy = new AtomicBoolean(false); @Lock(READ) public void doTimerWork() throws InterruptedException { if (!busy.compareAndSet(false, true)) { return; } try { System.out.println("Timer work started"); Thread.sleep(12000); System.out.println("Timer work done"); } finally { busy.set(false); } }}还有更多可能的变化,例如,您可以将忙碌检查委托给拦截器,或者将仅包含布尔值的单例注入计时器bean中,然后在其中检查该布尔值,等等。



