在ScheduledExecutorService中运行时,是否有一种很好的方法可以阻止任务内部重复任务?
让我们说,我有以下任务:
Future<?> f = scheduledExecutor.scheduleAtFixedRate(new Runnable() {
int count = 0;
public void run() {
System.out.println(count++);
if (count == 10) {
// ??? cancel self
}
}
}, 1, 1, TimeUnit.SECONDS);
从外面看,很容易通过f.cancel()取消,但是如何在指定的地方停止重复? (通过AtomicReference传递Future是不安全的,因为当scheduleAtFixedRate返回f迟到并且变量设置得晚,并且任务本身可能已经运行,在引用中看到null时,有一个潜在的窗口。)
答案 0 :(得分:13)
当重复任务抛出异常或错误时,它将被放置在Future中,并且不会再次重复该任务。您可以抛出您选择的RuntimeException或Error。
答案 1 :(得分:4)
您可以使用一个命名类,而不是使用匿名内部类,该类可以为您在安排任务时从Future
获取Executor
对象的属性。
abstract class FutureRunnable implements Runnable {
private Future<?> future;
/* Getter and Setter for future */
}
当您安排任务时,您可以将Future
传递给Runnable
。
FutureRunnable runnable = new FutureRunnable() {
public void run() {
if (/* abort condition */)
getFuture().cancel(false);
}
};
Future<?> future = executor.scheduleAtFixedRate(runnable, ...);
runnable.setFuture(future);
也许您必须确保在Future
设置之前未执行任务,否则您将获得NullPointerException
。
答案 2 :(得分:2)
对于Runnable而言,它似乎是一个糟糕的设计,无法知道它正在运行的执行程序,或者如果达到10不是错误状态就会抛出错误。
你可以在调度和执行之外进行10循环吗?这可能需要使用非调度执行程序,因为您自己手动调度它们。
答案 3 :(得分:1)
这是另一种方式,即使是线程安全;
final Future<?>[] f = {null};
f[0]= scheduledExecutor.scheduleAtFixedRate(new Runnable() {
int count = 0;
public void run() {
System.out.println(count++);
if (count == 10) {
Future<?> future;
while(null==(future = f[0])) Thread.yield();//prevent exceptionally bad thread scheduling
future.cancel(false);
return;
//cancel self
}
}
}, 1, 1, TimeUnit.SECONDS);
答案 4 :(得分:1)
刚看到这个...因为我想做同样的事情......这是我的解决方案,我怀疑这是线程安全的。
首先为Future创建一个容器:
public static class Cancel {
private ScheduledFuture<?> future;
public synchronized void setFuture(ScheduledFuture<?> future) {
this.future = future;
}
public synchronized void stop() {
LOG.debug("cancelling {}", future);
future.cancel(false);
}
}
然后是未来的代码:
final Cancel controller = new Cancel();
synchronized (controller) {
ScheduledFuture<?> future = scheduler.scheduleWithFixedDelay(() -> {
if (<CONTINUE RUNNING CONDITION) {
} else {
// STOP SCHEDULABLE FUTURE
controller.stop();
}
}, startTime, timeBetweenVisbilityChecks);
controller.setFuture(future);
}
}
请注意,在创建未来并在控制器上设置了未来之前,停止将不会被调用。
请记住,Runnable是一个不可思议的内部类,它将完全在另一个线程中运行。