Java 等待FutureTask上的取消()
我想取消从ThreadPoolExecutor获得的FutureTask,但我想确保线程池上的可调用线程已停止工作Java 等待FutureTask上的取消(),java,multithreading,concurrency,threadpool,futuretask,Java,Multithreading,Concurrency,Threadpool,Futuretask,我想取消从ThreadPoolExecutor获得的FutureTask,但我想确保线程池上的可调用线程已停止工作 如果我调用FutureTask#cancel(false)然后get()(阻止直到完成),我会得到一个canceledException。此异常是在任务停止执行后立即引发还是在任务停止执行后引发?它将在取消后立即引发 没有简单的方法可以知道它已经开始和结束了。您可以为自己创建一个包装器,以检查其状态 final AtomicInteger state = new AtomicInt
如果我调用FutureTask#cancel(false)然后get()(阻止直到完成),我会得到一个canceledException。此异常是在任务停止执行后立即引发还是在任务停止执行后引发?它将在取消后立即引发
没有简单的方法可以知道它已经开始和结束了。您可以为自己创建一个包装器,以检查其状态
final AtomicInteger state = new AtomicInteger();
// in the runnable
state.incrementAndGet();
try {
// do work
} finally {
state.decrementAdnGet();
}
是,
CancellationException
立即抛出。您可以扩展FutureTask以添加get()
方法的版本,该版本将等待Callable
的线程完成
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;
}
}
公共类ThreadWaitingFutureTask扩展了FutureTask{
专用最终信号量;
公共线程WaitingFutureTask(可调用可调用){
这个(可调用的新信号量(1));
}
public T getWithJoin()引发InterruptedException,ExecutionException{
试一试{
返回super.get();
}
捕获(取消异常e){
semaphore.acquire();
semaphore.release();
投掷e;
}
}
私有线程等待未来任务(最终可调用,
最终信号量(信号量){
super(新的可调用(){
public T call()引发异常{
semaphore.acquire();
试一试{
返回callable.call();
}
最后{
semaphore.release();
}
}
});
this.semaphore=信号量;
}
}
Aleksey的例子效果很好。我编写了一个变量,其中构造函数采用Runnable(将返回null)并显示如何在cancel()上直接阻塞(连接):
公共类FutureTask取消扩展FutureTask{
专用最终信号量;
public FutureTaskCancelWaitis(可运行可运行){
这(Executors.callable(runnable,(T)null));
}
public FutureTaskCancelWaitis(可调用可调用){
这个(可调用的新信号量(1));
}
@凌驾
公共布尔值取消(布尔值可能中断刷新){
//如果任务已成功取消,请在此处阻止,直到call()返回
如果(超级取消(可能中断刷新)){
试一试{
semaphore.acquire();
//一切都很好
返回true;
}捕捉(中断异常e){
//在等待时被打断。。。
}最后{
semaphore.release();
}
}
返回false;
}
private FutureTaskCancelWaits(最终可调用,最终信号量信号量){
super(新的可调用(){
public T call()引发异常{
semaphore.acquire();
试一试{
返回callable.call();
}最后{
semaphore.release();
}
}
});
this.semaphore=信号量;
}
}
此答案通过检查任务是否已在可调用内存中取消来修复Aleksey和FooJBar代码中的争用条件。(FutureTask.run检查状态和运行可调用项之间有一个窗口,在此期间cancel和getWithJoin都可以成功完成。但是,可调用项仍将运行。)
我还决定不覆盖原来的cancel,因为新的cancel需要声明InterruptedException
。新的“取消”将去掉其无用的返回值(因为true
可以表示“任务尚未开始”、“任务已开始并已造成大部分损坏”、“任务已开始并最终将完成”中的任意一个)。对super.cancel
返回值的检查也消失了,因此如果从不同线程多次调用新的cancel,它们都将等待任务完成
import java.util.concurrent.Callable;
import java.util.concurrent.CancellationException;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executors;
import java.util.concurrent.FutureTask;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
/**
* Based on: http://stackoverflow.com/questions/6040962/wait-for-cancel-on-futuretask
*
* @author Aleksandr Dubinsky
*/
public class FixedFutureTask<T> extends FutureTask<T> {
/**
* Creates a {@code FutureTask} that will, upon running, execute the given {@code Runnable},
* and arrange that {@code get} will return the given result on successful completion.
*
* @param runnable the runnable task
* @param result the result to return on successful completion.
* If you don't need a particular result, consider using constructions of the form:
* {@code Future<?> f = new FutureTask<Void>(runnable, null)}
* @throws NullPointerException if the runnable is null
*/
public
FixedFutureTask (Runnable runnable, T result) {
this (Executors.callable (runnable, result));
}
/**
* Creates a {@code FutureTask} that will, upon running, execute the given {@code Callable}.
*
* @param callable the callable task
* @throws NullPointerException if the callable is null
*/
public
FixedFutureTask (Callable<T> callable) {
this (new MyCallable (callable));
}
/** Some ugly code to work around the compiler's limitations on constructors */
private
FixedFutureTask (MyCallable<T> myCallable) {
super (myCallable);
myCallable.task = this;
}
private final Semaphore semaphore = new Semaphore(1);
private static class MyCallable<T> implements Callable<T>
{
MyCallable (Callable<T> callable) {
this.callable = callable;
}
final Callable<T> callable;
FixedFutureTask<T> task;
@Override public T
call() throws Exception {
task.semaphore.acquire();
try
{
if (task.isCancelled())
return null;
return callable.call();
}
finally
{
task.semaphore.release();
}
}
}
/**
* Waits if necessary for the computation to complete or finish cancelling, and then retrieves its result, if available.
*
* @return the computed result
* @throws CancellationException if the computation was cancelled
* @throws ExecutionException if the computation threw an exception
* @throws InterruptedException if the current thread was interrupted while waiting
*/
@Override public T
get() throws InterruptedException, ExecutionException, CancellationException {
try
{
return super.get();
}
catch (CancellationException e)
{
semaphore.acquire();
semaphore.release();
throw e;
}
}
/**
* Waits if necessary for at most the given time for the computation to complete or finish cancelling, and then retrieves its result, if available.
*
* @param timeout the maximum time to wait
* @param unit the time unit of the timeout argument
* @return the computed result
* @throws CancellationException if the computation was cancelled
* @throws ExecutionException if the computation threw an exception
* @throws InterruptedException if the current thread was interrupted while waiting
* @throws CancellationException
* @throws TimeoutException if the wait timed out
*/
@Override public T
get (long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, CancellationException, TimeoutException {
try
{
return super.get (timeout, unit);
}
catch (CancellationException e)
{
semaphore.acquire();
semaphore.release();
throw e;
}
}
/**
* Attempts to cancel execution of this task and waits for the task to complete if it has been started.
* If the task has not started when {@code cancelWithJoin} is called, this task should never run.
* If the task has already started, then the {@code mayInterruptIfRunning} parameter determines
* whether the thread executing this task should be interrupted in an attempt to stop the task.
*
* <p>After this method returns, subsequent calls to {@link #isDone} will
* always return {@code true}. Subsequent calls to {@link #isCancelled}
* will always return {@code true} if this method returned {@code true}.
*
* @param mayInterruptIfRunning {@code true} if the thread executing this task should be interrupted;
* otherwise, in-progress tasks are allowed to complete
* @throws InterruptedException if the thread is interrupted
*/
public void
cancelAndWait (boolean mayInterruptIfRunning) throws InterruptedException {
super.cancel (mayInterruptIfRunning);
semaphore.acquire();
semaphore.release();
}
}
import java.util.concurrent.Callable;
导入java.util.concurrent.CancellationException;
导入java.util.concurrent.ExecutionException;
导入java.util.concurrent.Executors;
导入java.util.concurrent.FutureTask;
导入java.util.concurrent.Semaphore;
导入java.util.concurrent.TimeUnit;
导入java.util.concurrent.TimeoutException;
/**
*基于:http://stackoverflow.com/questions/6040962/wait-for-cancel-on-futuretask
*
*@作者Aleksandr Dubinsky
*/
公共类FixedFutureTask扩展了FutureTask{
/**
*创建一个{@code FutureTask},它将在运行时执行给定的{@code Runnable},
*并安排{@code get}在成功完成时返回给定的结果。
*
*@param runnable可运行任务
*@param result成功完成后返回的结果。
*如果您不需要特定的结果,请考虑使用窗体的结构:
*{@code Future f=new FutureTask(可运行,空)}
*@如果runnable为null,则抛出NullPointerException
*/
公开的
FixedFutureTask(可运行,可运行,T结果){
这(Executors.callable(runnable,result));
}
/**
*创建一个{@code FutureTask},它将在运行时执行给定的{@code Callable}。
*
*@param callable可调用的任务
*@如果可调用项为null,则抛出NullPointerException
*/
公开的
FixedFutureTask(可调用可调用){
此(新MyCallable(callable));
}
/**一些丑陋的代码可以绕过编译器对构造函数的限制*/
私有的
FixedFutureTask(MyCallable MyCallable){
超级(myCallable);
myCallable.task=这个;
}
专用最终信号量信号量=新信号量(1);
私有静态类MyCallable实现了Callable
{
MyCallable(可调用可调用){
import java.util.concurrent.Callable;
import java.util.concurrent.CancellationException;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executors;
import java.util.concurrent.FutureTask;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
/**
* Based on: http://stackoverflow.com/questions/6040962/wait-for-cancel-on-futuretask
*
* @author Aleksandr Dubinsky
*/
public class FixedFutureTask<T> extends FutureTask<T> {
/**
* Creates a {@code FutureTask} that will, upon running, execute the given {@code Runnable},
* and arrange that {@code get} will return the given result on successful completion.
*
* @param runnable the runnable task
* @param result the result to return on successful completion.
* If you don't need a particular result, consider using constructions of the form:
* {@code Future<?> f = new FutureTask<Void>(runnable, null)}
* @throws NullPointerException if the runnable is null
*/
public
FixedFutureTask (Runnable runnable, T result) {
this (Executors.callable (runnable, result));
}
/**
* Creates a {@code FutureTask} that will, upon running, execute the given {@code Callable}.
*
* @param callable the callable task
* @throws NullPointerException if the callable is null
*/
public
FixedFutureTask (Callable<T> callable) {
this (new MyCallable (callable));
}
/** Some ugly code to work around the compiler's limitations on constructors */
private
FixedFutureTask (MyCallable<T> myCallable) {
super (myCallable);
myCallable.task = this;
}
private final Semaphore semaphore = new Semaphore(1);
private static class MyCallable<T> implements Callable<T>
{
MyCallable (Callable<T> callable) {
this.callable = callable;
}
final Callable<T> callable;
FixedFutureTask<T> task;
@Override public T
call() throws Exception {
task.semaphore.acquire();
try
{
if (task.isCancelled())
return null;
return callable.call();
}
finally
{
task.semaphore.release();
}
}
}
/**
* Waits if necessary for the computation to complete or finish cancelling, and then retrieves its result, if available.
*
* @return the computed result
* @throws CancellationException if the computation was cancelled
* @throws ExecutionException if the computation threw an exception
* @throws InterruptedException if the current thread was interrupted while waiting
*/
@Override public T
get() throws InterruptedException, ExecutionException, CancellationException {
try
{
return super.get();
}
catch (CancellationException e)
{
semaphore.acquire();
semaphore.release();
throw e;
}
}
/**
* Waits if necessary for at most the given time for the computation to complete or finish cancelling, and then retrieves its result, if available.
*
* @param timeout the maximum time to wait
* @param unit the time unit of the timeout argument
* @return the computed result
* @throws CancellationException if the computation was cancelled
* @throws ExecutionException if the computation threw an exception
* @throws InterruptedException if the current thread was interrupted while waiting
* @throws CancellationException
* @throws TimeoutException if the wait timed out
*/
@Override public T
get (long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, CancellationException, TimeoutException {
try
{
return super.get (timeout, unit);
}
catch (CancellationException e)
{
semaphore.acquire();
semaphore.release();
throw e;
}
}
/**
* Attempts to cancel execution of this task and waits for the task to complete if it has been started.
* If the task has not started when {@code cancelWithJoin} is called, this task should never run.
* If the task has already started, then the {@code mayInterruptIfRunning} parameter determines
* whether the thread executing this task should be interrupted in an attempt to stop the task.
*
* <p>After this method returns, subsequent calls to {@link #isDone} will
* always return {@code true}. Subsequent calls to {@link #isCancelled}
* will always return {@code true} if this method returned {@code true}.
*
* @param mayInterruptIfRunning {@code true} if the thread executing this task should be interrupted;
* otherwise, in-progress tasks are allowed to complete
* @throws InterruptedException if the thread is interrupted
*/
public void
cancelAndWait (boolean mayInterruptIfRunning) throws InterruptedException {
super.cancel (mayInterruptIfRunning);
semaphore.acquire();
semaphore.release();
}
}
CompletionService<List<DeviceInfo>> completionService =
new MyCompletionService<>(Executors.newCachedThreadPool());
Future task = completionService.submit(yourTask);
try {
LogHelper.i(TAG, "result 111: " );
Future<List<DeviceInfo>> result = completionService.take();
LogHelper.i(TAG, "result: " + result.get());
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
import java.util.concurrent.AbstractExecutorService;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.Callable;
import java.util.concurrent.CancellationException;
import java.util.concurrent.CompletionService;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executor;
import java.util.concurrent.Future;
import java.util.concurrent.FutureTask;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.RunnableFuture;
import java.util.concurrent.TimeUnit;
/**
* This is a CompletionService like java.util.ExecutorCompletionService, but we can get partly computed result
* from our FutureTask which returned from submit, even we cancel or interrupt it.
* Besides, CompletionService can ensure that the FutureTask is done when we get from take or poll method.
*/
public class MyCompletionService<V> implements CompletionService<V> {
private final Executor executor;
private final AbstractExecutorService aes;
private final BlockingQueue<Future<V>> completionQueue;
/**
* FutureTask extension to enqueue upon completion.
*/
private static class QueueingFuture<V> extends FutureTask<Void> {
QueueingFuture(RunnableFuture<V> task,
BlockingQueue<Future<V>> completionQueue) {
super(task, null);
this.task = task;
this.completionQueue = completionQueue;
}
private final Future<V> task;
private final BlockingQueue<Future<V>> completionQueue;
protected void done() { completionQueue.add(task); }
}
private static class DoneFutureTask<V> extends FutureTask<V> {
private Object outcome;
DoneFutureTask(Callable<V> task) {
super(task);
}
DoneFutureTask(Runnable task, V result) {
super(task, result);
}
@Override
protected void set(V v) {
super.set(v);
outcome = v;
}
@Override
public V get() throws InterruptedException, ExecutionException {
try {
return super.get();
} catch (CancellationException e) {
return (V)outcome;
}
}
}
private RunnableFuture<V> newTaskFor(Callable<V> task) {
return new DoneFutureTask<V>(task);
}
private RunnableFuture<V> newTaskFor(Runnable task, V result) {
return new DoneFutureTask<V>(task, result);
}
/**
* Creates an MyCompletionService using the supplied
* executor for base task execution and a
* {@link LinkedBlockingQueue} as a completion queue.
*
* @param executor the executor to use
* @throws NullPointerException if executor is {@code null}
*/
public MyCompletionService(Executor executor) {
if (executor == null)
throw new NullPointerException();
this.executor = executor;
this.aes = (executor instanceof AbstractExecutorService) ?
(AbstractExecutorService) executor : null;
this.completionQueue = new LinkedBlockingQueue<Future<V>>();
}
/**
* Creates an MyCompletionService using the supplied
* executor for base task execution and the supplied queue as its
* completion queue.
*
* @param executor the executor to use
* @param completionQueue the queue to use as the completion queue
* normally one dedicated for use by this service. This
* queue is treated as unbounded -- failed attempted
* {@code Queue.add} operations for completed tasks cause
* them not to be retrievable.
* @throws NullPointerException if executor or completionQueue are {@code null}
*/
public MyCompletionService(Executor executor,
BlockingQueue<Future<V>> completionQueue) {
if (executor == null || completionQueue == null)
throw new NullPointerException();
this.executor = executor;
this.aes = (executor instanceof AbstractExecutorService) ?
(AbstractExecutorService) executor : null;
this.completionQueue = completionQueue;
}
public Future<V> submit(Callable<V> task) {
if (task == null) throw new NullPointerException();
RunnableFuture<V> f = newTaskFor(task);
executor.execute(new QueueingFuture<V>(f, completionQueue));
return f;
}
public Future<V> submit(Runnable task, V result) {
if (task == null) throw new NullPointerException();
RunnableFuture<V> f = newTaskFor(task, result);
executor.execute(new QueueingFuture<V>(f, completionQueue));
return f;
}
public Future<V> take() throws InterruptedException {
return completionQueue.take();
}
public Future<V> poll() {
return completionQueue.poll();
}
public Future<V> poll(long timeout, TimeUnit unit)
throws InterruptedException {
return completionQueue.poll(timeout, unit);
}
}