我有两个任务:第一个任务(work
)重新出现,第二个任务(cleanup
)释放一些资源。在重复发生cleanup
任务完成后,work
任务应该只运行一次,并且不会再次运行。
我的第一直觉是这样的:
ScheduledExecutorService service = ...;
ScheduledFuture<?> future = service.scheduleAtFixedRate(work, ...);
// other stuff happens
future.cancel(false);
cleanup.run();
这里的问题是cancel()
立即返回。因此,如果work
恰好在运行,那么cleanup
将重叠它。
理想情况下,我会使用Guava的Futures.addCallback(ListenableFuture future, FutureCallback callback)
之类的东西。 (番石榴15可能有something like that)。
与此同时,当future
取消且 work
不再运行时,如何触发回调?
答案 0 :(得分:1)
这是我提出的解决方案。它似乎很简单,但我仍然认为那里有一个更常见和/或更优雅的解决方案。我真的很想在像Guava这样的库中看到一个......
首先,我创建一个包装器,在我的Runnables上强加互斥:
private static final class SynchronizedRunnable implements Runnable {
private final Object monitor;
private final Runnable delegate;
private SynchronizedRunnable(Object monitor, Runnable delegate) {
this.monitor = monitor;
this.delegate = delegate;
}
@Override
public void run() {
synchronized (monitor) {
delegate.run();
}
}
}
然后我创建一个包装器来激活我对cancel
成功调用的回调:
private static final class FutureWithCancelCallback<V> extends ForwardingFuture.SimpleForwardingFuture<V> {
private final Runnable callback;
private FutureWithCancelCallback(Future<V> delegate, Runnable callback) {
super(delegate);
this.callback = callback;
}
@Override
public boolean cancel(boolean mayInterruptIfRunning) {
boolean cancelled = super.cancel(mayInterruptIfRunning);
if (cancelled) {
callback.run();
}
return cancelled;
}
}
然后我用自己的方法将它们全部卷起来:
private Future<?> scheduleWithFixedDelayAndCallback(ScheduledExecutorService service, Runnable work, long initialDelay, long delay, TimeUnit unit, Runnable cleanup) {
Object monitor = new Object();
Runnable monitoredWork = new SynchronizedRunnable(monitor, work);
Runnable monitoredCleanup = new SynchronizedRunnable(monitor, cleanup);
Future<?> rawFuture = service.scheduleAtFixedRate(monitoredWork, initialDelay, delay, unit);
Future<?> wrappedFuture = new FutureWithCancelCallback(rawFuture, monitoredCleanup);
return wrappedFuture;
}
答案 1 :(得分:1)
然后我会再拍一次。您可以增强命令,也可以包装已执行的Runnable
/ Callable
。看看这个:
public static class RunnableWrapper implements Runnable {
private final Runnable original;
private final Lock lock = new ReentrantLock();
public RunnableWrapper(Runnable original) {
this.original = original;
}
public void run() {
lock.lock();
try {
this.original.run();
} finally {
lock.unlock();
}
}
public void awaitTermination() {
lock.lock();
try {
} finally {
lock.unlock();
}
}
}
因此您可以将代码更改为
ScheduledExecutorService service = ...;
RunnableWrapper wrapper = new RunnableWrapper(work);
ScheduledFuture<?> future = service.scheduleAtFixedRate(wrapper, ...);
// other stuff happens
future.cancel(false);
wrapper.awaitTermination();
cleanup.run();
调用cancel
后,work
不再运行,awaitTermination()
立即返回,或者正在运行,awaitTermination()
会阻止,直到完成为止。
答案 2 :(得分:0)
你为什么不这样做
// other stuff happens
future.cancel(false);
service.shutdown();
service.awaitTermination(1, TimeUnit.DAYS);
cleanup.run();
这将告诉您的执行程序服务关闭,从而允许您等待可能正在运行的work
完成。