Wait for cancel() on FutureTask
Yes, CancellationException
is thrown immediately. You may extend FutureTask to add get()
method's version which waits until Callable
's thread is finished.
public class ThreadWaitingFutureTask<T> extends FutureTask<T> { private final Semaphore semaphore; public ThreadWaitingFutureTask(Callable<T> callable) { this(callable, new Semaphore(1)); } public T getWithJoin() throws InterruptedException, ExecutionException { try { return super.get(); } catch (CancellationException e) { semaphore.acquire(); semaphore.release(); throw e; } } private ThreadWaitingFutureTask(final Callable<T> callable, final Semaphore semaphore) { super(new Callable<T>() { public T call() throws Exception { semaphore.acquire(); try { return callable.call(); } finally { semaphore.release(); } } }); this.semaphore = semaphore; }}
Aleksey's example works well. I wrote a variant with a constructor taking a Runnable (will return null) and showing how to directly block (join) on cancel():
public class FutureTaskCancelWaits<T> extends FutureTask<T> { private final Semaphore semaphore; public FutureTaskCancelWaits(Runnable runnable) { this(Executors.callable(runnable, (T) null)); } public FutureTaskCancelWaits(Callable<T> callable) { this(callable, new Semaphore(1)); } @Override public boolean cancel(boolean mayInterruptIfRunning) { // If the task was successfully cancelled, block here until call() returns if (super.cancel(mayInterruptIfRunning)) { try { semaphore.acquire(); // All is well return true; } catch (InterruptedException e) { // Interrupted while waiting... } finally { semaphore.release(); } } return false; } private FutureTaskCancelWaits(final Callable<T> callable, final Semaphore semaphore) { super(new Callable<T>() { public T call() throws Exception { semaphore.acquire(); try { return callable.call(); } finally { semaphore.release(); } } }); this.semaphore = semaphore; }}
It is thrown as soon as it is cancelled.
There is no easy way to know it has started and is finished. You can create a wrapper for you runnable to check its state.
final AtomicInteger state = new AtomicInteger();// in the runnablestate.incrementAndGet();try { // do work} finally { state.decrementAdnGet();}