EJB @Schedule等到方法完成
2022-09-01 04:14:58
我想写一个后台作业(EJB 3.1),它每分钟执行一次。为此,我使用以下注释:
@Schedule(minute = "*/1", hour = "*")
这很好用。
但是,有时作业可能需要一分钟以上。在这种情况下,计时器仍会触发,从而导致线程问题。
如果当前执行未完成,是否可以以某种方式终止调度程序?
我想写一个后台作业(EJB 3.1),它每分钟执行一次。为此,我使用以下注释:
@Schedule(minute = "*/1", hour = "*")
这很好用。
但是,有时作业可能需要一分钟以上。在这种情况下,计时器仍会触发,从而导致线程问题。
如果当前执行未完成,是否可以以某种方式终止调度程序?
如果只有 1 个计时器可能同时处于活动状态,则有几种解决方案。
首先,可能应该出现在 .在 Singleton 中,方法默认为写锁定,因此在尝试调用计时器方法时,容器将自动锁定,而其中仍有活动。@Timer
@Singleton
以下基本够用:
@Singleton
public 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不允许将读锁定升级到写锁定)。
计时器豆:
@Singleton
public 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");
}
}
}
工蜂:
@Singleton
public class WorkerBean {
@AccessTimeout(0)
public void doTimerWork() throws InterruptedException {
System.out.println("Timer work started");
Thread.sleep(12000);
System.out.println("Timer work done");
}
}
这可能仍然会在日志中打印一个嘈杂的异常,因此一个更详细但更安静的解决方案是使用显式布尔值:
计时器豆:
@Singleton
public class TimerBean {
@EJB
private WorkerBean workerBean;
@Lock(READ)
@Schedule(second = "*/5", minute = "*", hour = "*", persistent = false)
public void atSchedule() {
workerBean.doTimerWork();
}
}
工蜂:
@Singleton
public 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,并在那里检查布尔值等。
我遇到了同样的问题,但解决方式略有不同。
@Singleton
public class DoStuffTask {
@Resource
private TimerService timerSvc;
@Timeout
public void doStuff(Timer t) {
try {
doActualStuff(t);
} catch (Exception e) {
LOG.warn("Error running task", e);
}
scheduleStuff();
}
private void doActualStuff(Timer t) {
LOG.info("Doing Stuff " + t.getInfo());
}
@PostConstruct
public void initialise() {
scheduleStuff();
}
private void scheduleStuff() {
timerSvc.createSingleActionTimer(1000l, new TimerConfig());
}
public void stop() {
for(Timer timer : timerSvc.getTimers()) {
timer.cancel();
}
}
}
这可以通过设置将来要执行的任务(在本例中为一秒钟)来工作。在任务结束时,它会再次计划任务。
编辑:更新以将“东西”重构为另一种方法,以便我们可以保护异常,以便计时器的重新调度始终发生