5

我有两个任务:第一个任务 ( work) 正在重复发生,第二个任务 ( cleanup) 正在释放一些资源。该cleanup任务应在重复任务完成后恰好运行一次,work并且不会再次运行。

我的第一直觉是这样的:

ScheduledExecutorService service = ...;
ScheduledFuture<?> future = service.scheduleAtFixedRate(work, ...);

// other stuff happens

future.cancel(false);
cleanup.run();

这里的问题是cancel()立即返回。所以如果work碰巧正在运行,那么cleanup就会重叠它。

理想情况下,我会使用 Guava's 之类的东西Futures.addCallback(ListenableFuture future, FutureCallback callback)。(番石榴 15 可能有类似的东西)。

future同时,当被取消并且 work不再运行时如何触发回调?

4

3 回答 3

1

这是我想出的解决方案。这似乎很简单,但我仍然认为那里有更常见和/或优雅的解决方案。我真的很想在像番石榴这样的图书馆里看到一个……

首先,我创建一个包装器来对我的 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;
}
于 2013-04-03T18:30:34.857 回答
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()阻塞直到完成。

于 2013-04-04T09:40:55.403 回答
0

你为什么不做

// other stuff happens

future.cancel(false);
service.shutdown();
service.awaitTermination(1, TimeUnit.DAYS);
cleanup.run();

这将告诉您的执行程序服务关闭,从而允许您等待可能的运行work完成。

于 2013-04-03T20:49:18.260 回答