Java 取消FutureTask后从中获取结果

Java 取消FutureTask后从中获取结果,java,concurrency,futuretask,Java,Concurrency,Futuretask,考虑在可调用的实例中运行很长时间的计算 并认为该计算的结果可以根据计算时间而具有一定的精度,即:如果任务将被取消,则它应该返回到取消之前计算的(例如,我们有一个无理数计算的传送器)。 希望使用标准的java并发UTIL实现这个范例,例如 Callable<ValuableResult> task = new Callable<>() { ... }; Future<ValuableResult> future = Executors.newSingleThre

考虑在
可调用的
实例中运行很长时间的计算

并认为该计算的结果可以根据计算时间而具有一定的精度,即:如果任务将被取消,则它应该返回到取消之前计算的(例如,我们有一个无理数计算的传送器)。 希望使用标准的java并发UTIL实现这个范例,例如

Callable<ValuableResult> task = new Callable<>() { ... };
Future<ValuableResult> future = Executors.newSingleThreadExecutor().submit(task);
try {
    return future.get(timeout, TimeUnit.SECONDS);
} catch (TimeoutException te) {
    future.cancel(true);
    // HERE! Get what was computed so far
}
Callable task=newcallable(){…};
Future Future=Executors.newSingleThreadExecutor().submit(任务);
试一试{
返回future.get(超时、时间单位、秒);
}捕获(TimeoutException te){
future.cancel(true);
//这里!获取到目前为止的计算结果
}

似乎,如果不完全重新实现
Future
ThreadPoolExecutor
接口,这个问题就无法解决。Java 1.7中有任何方便的现有工具吗?

不要通过未来的API取消它,而是告诉它通过自己的机制完成(例如传递给构造函数的
long
,告诉它在正常返回之前要运行多长时间;或者设置为true的
AtomicBoolean


请记住,一旦任务实际启动,
cancel(true)
不会神奇地停止它。然后它所做的就是中断线程。有几种方法可以检查此标志并抛出InterruptedException,但否则您必须手动检查isInterrupted标志。因此,既然您需要对协作机制进行编码,为什么不让它更适合您的需求呢?

在我看来,在这种情况下,最简单的方法是准备一些
最终的
ResultWrapper
对象,这些对象将在
可调用的
实例中传递:

final ValuableResultWrapper wrapper = new ValuableResultWrapper();
final CountDownLatch latch = new CountDownLatch(1);

Callable<ValuableResultWrapper> task = new Callable<>() { 
   ... 
   wrapper.setValue(...); // here we set what we have computed so far
   latch.countDown();
   return wrapper;
   ...  
};
Future<ValuableResultWrapper> future = Executors.newSingleThreadExecutor().submit(task);
try {
    return future.get(timeout, TimeUnit.SECONDS);
} catch (TimeoutException te) {
    future.cancel(true);
    // HERE! Get what was computed so far
    latch.await();
    return wrapper;
}
final valuableResultRapper包装器=新的valuableResultRapper();
最终倒计时闩锁=新倒计时闩锁(1);
可调用任务=新可调用(){
... 
wrapper.setValue(…);//这里我们设置了到目前为止计算的内容
倒计时();
返回包装器;
...  
};
Future Future=Executors.newSingleThreadExecutor().submit(任务);
试一试{
返回future.get(超时、时间单位、秒);
}捕获(TimeoutException te){
future.cancel(true);
//这里!获取到目前为止的计算结果
satch.wait();
返回包装器;
}

UPD:在这种实现(变得复杂)中,我们必须引入某种锁存器(
CountDownLatch
,在我的示例中),以确保该任务在我们完成
返回包装器之前完成
CompletionSerivce
比仅
未来任务更强大,而且在许多情况下更合适。我从中得到一些解决问题的想法。此外,它的子类public
ExecutorCompletionService
比FutureTask简单,只包含几行代码。它很容易阅读。所以我修改了类以获得部分计算结果。毕竟,这是一个令我满意的解决方案,它看起来简单明了

演示代码:

CompletionService<List<DeviceInfo>> completionService =
                new MyCompletionService<>(Executors.newCachedThreadPool());   
        Future task = completionService.submit(detector);
    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();
    }
CompletionService CompletionService=
新的MyCompletionService(Executors.newCachedThreadPool());
未来任务=completionService.submit(检测器);
试一试{
LogHelper.i(标记“result 111:”);
未来结果=completionService.take();
LogHelper.i(标记,“result:+result.get());
}捕捉(中断异常e){
e、 printStackTrace();
}捕获(执行例外){
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);
    }

}
导入java.util.concurrent.AbstractExecutorService;
导入java.util.concurrent.BlockingQueue;
导入java.util.concurrent.Callable;
导入java.util.concurrent.CancellationException;
导入java.util.concurrent.CompletionService;
导入java.util.concurrent.ExecutionException;
导入java.util.concurrent.Executor;
导入java.util.concurrent.Future;
导入java.util.concurrent.FutureTask;
导入java.util.concurrent.LinkedBlockingQueue;
导入java.util.concurrent.RunnableFuture;
导入java.util.concurrent.TimeUnit;
/**
*这是一个类似java.util.ExecutionCompletionService的CompletionService,但我们可以得到部分计算结果
*从提交返回的未来任务中,我们甚至取消或中断了它。
*此外,CompletionService可以确保将来的任务在我们使用take或poll方法时完成。
*/
公共类MyCompletionService实现CompletionService{
私人最终执行人;
私人最终服务aes;
私有最终阻塞队列完成队列;
/**
*未来任务扩展到完成后排队。
*/
私有静态类QueueingFuture扩展FutureTask{
QueueingFuture(RunnableFuture任务,
阻塞队列完成队列){
super(任务,空);
this.task=任务;
this.completionQueue=completionQueue;
}
私人最终未来任务;
私有最终阻塞队列完成队列;
受保护的void done(){completionQueue.add(task);}
}
私有静态类DoneFutureTask扩展了FutureTask{
私人客体结果;
DoneFutureTask(可调用任务){
超级(任务);
}
DoneFutureTask(可运行任务,V结果){
超级(任务、结果);
}
@凌驾
受保护的无效集(V){
super.set(v);
结果=v;
}
@凌驾
public V get()抛出InterruptedException,ExecutionException{
试一试{
返回super.get();
}捕获(取消异常e){
返回(V)结果;
}
}
}
私有RunnableFuture newTaskFor(可调用任务){
返回新的DoneFutureTask(task);
}
私人运营未来新机场(Ru)