[英]EJB @Schedule wait until method completed
我想編寫一個每分鍾執行一次的后台作業 (EJB 3.1)。 為此,我使用以下注釋:
@Schedule(minute = "*/1", hour = "*")
這是工作正常。
但是,有時這項工作可能需要一分鍾以上。 在這種情況下,計時器仍會被觸發,從而導致線程問題。
如果當前執行未完成,是否有可能終止調度程序?
如果只有 1 個計時器可能同時處於活動狀態,則有幾種解決方案。
所有的第一@Timer
大概應該是存在於一個@Singleton
。 在 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();
}
}
}
這通過設置要在將來執行的任務(在本例中為一秒)來實現。 在任務結束時,它會再次安排任務。
編輯:更新以將“東西”重構為另一種方法,以便我們可以防范異常,以便始終重新安排計時器
從 Java EE 7 開始,可以使用“EE-aware” ManagedScheduledExecutorService ,即在 WildFly 中:
例如,在@Singleton @Startup @LocalBean
,注入在standalone.xml
配置的默認“managed-scheduled-executor-service”:
@Resource
private ManagedScheduledExecutorService scheduledExecutorService;
在@PostConstruct
安排一些任務以固定延遲執行,即每秒執行一次:
scheduledExecutorService.scheduleWithFixedDelay(this::someMethod, 1, 1, TimeUnit.SECONDS);
創建並執行一個周期性動作,該動作首先在給定的初始延遲后啟用,然后在一個執行終止和下一個執行開始之間具有給定的延遲。[...]
不要在 ie @PreDestroy
關閉調度程序:
Managed Scheduled Executor Service 實例由應用服務器管理,因此 Java EE 應用程序被禁止調用任何與生命周期相關的方法。
好吧,我遇到了類似的問題。 有一個作業應該每 30 分鍾運行一次,有時該作業需要 30 多分鍾才能完成,在這種情況下,另一個作業實例正在啟動,而前一個作業尚未完成。 我通過使用一個靜態布爾變量解決了這個問題,每當它開始運行時我的工作就會將它設置為 true,然后在它完成時將它設置回 false。 由於它是一個靜態變量,所有實例將始終看到相同的副本。 當您設置和取消設置靜態變量時,您甚至可以同步塊。 class myjob{ private static boolean isRunning=false;
public executeJob(){
if (isRunning)
return;
isRunning=true;
//execute job
isRunning=false;
}
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.