我想写一个后台作业(EJB3.1),它每分钟执行一次。为此,我使用以下注释:
@Schedule(minute = "*/1", hour = "*")运行得很好。
但是,有时这项工作可能需要一分钟以上的时间。在这种情况下,计时器仍然被触发,从而导致线程问题。
如果当前的执行没有完成,有没有可能终止调度器?
发布于 2013-01-19 20:44:51
如果同时只有一个定时器处于活动状态,那么有几种解决方案。
首先,@Timer可能应该出现在@Singleton上。在Singleton中,方法在默认情况下是写锁定的,所以当尝试调用timer方法时,容器将自动锁定,而其中仍有活动。
以下内容基本上就足够了:
@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是必需的,因为EJB3.1不允许将读锁升级为写锁)。
计时器bean:
@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");
}
}
}工作bean:
@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");
}
}这可能仍然会在日志中打印一个嘈杂的异常,所以一个更详细但更安静的解决方案是使用显式布尔值:
计时器bean:
@Singleton
public class TimerBean {
@EJB
private WorkerBean workerBean;
@Lock(READ)
@Schedule(second = "*/5", minute = "*", hour = "*", persistent = false)
public void atSchedule() {
workerBean.doTimerWork();
}
}工作bean:
@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中,然后检查那里的布尔值,等等。
发布于 2013-12-06 23:26:35
我遇到了同样的问题,但解决方法略有不同。
@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();
}
}
}这是通过设置一个在将来执行的任务(在本例中是在一秒内执行)来实现的。在任务结束时,它会再次调度任务。
编辑:更新以将“内容”重构到另一个方法中,这样我们就可以保护异常,从而使计时器的重新调度始终发生。
发布于 2016-10-07 18:09:45
从JavaEE7开始,就可以使用“支持EE的”ManagedScheduledExecutorService,即在WildFly中:
例如,在一个服务中,注入standalone.xml中配置的缺省“managed-scheduled-executor- @Singleton @Startup @LocalBean”
@Resource
private ManagedScheduledExecutorService scheduledExecutorService;在@PostConstruct中调度要执行的任务,即使用固定延迟每秒执行一次
scheduledExecutorService.scheduleWithFixedDelay(this::someMethod, 1, 1, TimeUnit.SECONDS);创建并执行一个周期性操作,该操作首先在给定的初始延迟之后启用,随后在一个执行的终止和下一个执行的开始之间具有给定的延迟。
请勿关闭@PreDestroy中的调度程序
托管的调度执行器服务实例由应用程序服务器管理,因此禁止Java应用程序调用任何与生命周期相关的方法。
https://stackoverflow.com/questions/14402068
复制相似问题