В чистом объектно-ориентированном мире геттеры и сеттеры - страшный анти-шаблон. Прочтите эту статью: Getters / Setters. Зло. Период . В двух словах они поощряют программистов думать об объектах как о структурах данных, и этот тип мышления является чисто процедурным (например, в COBOL или C). На объектно-ориентированном языке отсутствуют структуры данных, но только объекты, которые выставляют поведение (не атрибуты / свойства!) [/ G2]
. Вы можете найти больше о них в разделе 3.5 из Элегантные объекты (моя книга об объектно-ориентированном программировании).
Если только один таймер может когда-либо быть активным одновременно, есть несколько решений.
Прежде всего @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
по умолчанию заблокирован по записи, и в нем может быть только один поток, включая вызовы, инициированные контейнером.
После блокировки контейнер может повторить попытку таймера, поэтому, чтобы предотвратить это, вы вместо этого воспользуетесь блокировкой чтения и делегируете второй компонент (требуется второй компонент, потому что 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);
}
}
}
Возможны еще несколько вариантов, например вы можете делегировать занятую проверку перехватчику или вводить синглтон, который содержит только логическое значение в компоненте таймера, и проверять, что boolean существует и т. д.
Я столкнулся с одной и той же проблемой, но решил ее немного по-другому.
@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();
}
}
}
Это работает, настраивая задачу для выполнения в будущем (в данном случае за одну секунду). В конце задачи он снова планирует задачу.
EDIT: Обновлено для рефакторинга «материала» в другой метод, чтобы мы могли защищать исключения, чтобы всегда происходило перепланирование таймера
Ну, у меня была аналогичная проблема. Было задание, которое должно было запускаться каждые 30 минут, и иногда работа занимала более 30 минут, чтобы завершить в этом случае другой экземпляр задания начинался, пока предыдущий еще не был завершен. Я решил это, имея статическую логическую переменную, которую моя задача будет устанавливать на true, когда она запустится, а затем вернет ее значение false до тех пор, пока она не закончится. Поскольку его статическая переменная, все экземпляры будут видеть одну и ту же копию во все времена. Вы даже можете синхронизировать блок, когда u установлен и отключить статическую переменную. class myjob {private static boolean isRunning = false;
public executeJob(){
if (isRunning)
return;
isRunning=true;
//execute job
isRunning=false;
}
}
Так как Java EE 7 можно использовать «EE-aware» ManagedScheduledExecutorService , то есть в WildFly:
В, например, в @Singleton @Startup @LocalBean
, введите значение по умолчанию " настроенный в standalone.xml
:
@Resource
private ManagedScheduledExecutorService scheduledExecutorService;
Запланируйте выполнение какой-либо задачи в @PostConstruct
, т.е. каждую секунду с фиксированной задержкой:
scheduledExecutorService.scheduleWithFixedDelay(this::someMethod, 1, 1, TimeUnit.SECONDS);
Создает и выполняет периодическое действие, которое активируется сначала после заданной начальной задержки, а затем с указанной задержкой между завершением одного выполнения и начало следующего. [...]
blockquote>Не закрывать планировщик, т. е.
@PreDestroy
:Управляемые экземпляры Службы экстренных служб контролируются сервер приложений, поэтому приложениям Java EE запрещается ссылаться на любой связанный с жизненным циклом метод.
blockquote>