Callback when a periodic task is canceled and executed.

I have two tasks: the first task ( work) is repeated, and the second task ( cleanup) is releasing some resources. The task cleanupmust be executed exactly once after the completion of the repeated task workand will not be performed again.

My first instinct was something like this:

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

// other stuff happens

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

The problem is that it cancel()returns immediately. Therefore, if workexecuted, then it cleanupwill overlap it.

Ideally, I would use something like Guava Futures.addCallback(ListenableFuture future, FutureCallback callback). (Guava 15 may be something like this ).

In the meantime, how can I launch a callback when futurecanceled and workno longer works?

+5
3

, . , , / . , 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

. , 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() .

+1

Why don't you do

// other stuff happens

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

This will shut down your executing service, allowing you to wait for the shutdown work.

0
source

All Articles