从ScheduledExecutorService中运行的任务本身中停止定期任务
在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。)
当重复任务抛出exception或错误时,它将被放置在Future中,并且不会再次重复该任务。 您可以抛出您选择的RuntimeException或Error。
您可以使用命名类,而不是使用匿名内部类,该类可以为您在安排任务时从Executor
获取的Future
对象具有属性。
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
。
看起来好的设计让Runnable知道它正在运行的执行器的任何信息,或者如果达到10不是错误状态则抛出错误是一个hack。
你可以在调度和执行之外循环到10吗? 这可能需要使用非调度执行程序,因为您自己手动调度它们。
这是另一种方式,即使是线程安全;
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);
刚看到这个…因为我想做同样的事情……这是我的解决方案,我怀疑这是线程安全的。
首先为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 (
因此,请注意在创建未来并在控制器上设置了未来之前,停止将不会被调用。
请记住,Runnable是一个非同类的内部类,它将完全在另一个线程中运行。