一尘不染

扩展FutureTask,如何处理取消

java

FutureTask已从java.util.concurrent提供回调来跟踪提交给的任务的执行ExecutorService

public class StatusTask<V> extends FutureTask<V> {

    private final ITaskStatusHandler<V> statusHandler;

    public StatusTask(Callable<V> callable, ITaskStatusHandler<V> statusHandler){
        super(callable);
        if (statusHandler == null)
            throw new NullPointerException("statusHandler cannot be null");
        this.statusHandler = statusHandler;
        statusHandler.TaskCreated(this);
    }

    @Override
    public void run() {
        statusHandler.TaskRunning(this);
        super.run();
    }

    @Override
    protected void done() {
        super.done();
        statusHandler.TaskCompleted(this);
    }

}

现在,我看到的是任务是否已提交,但最终进入队列,并且cancel(true);任务run()仍被调用FutureTask.run()(方法仍然被调用),并且(可能)检查任务已取消且未调用包装的可调用对象。

我应该做例如

@Override
public void run() {
  if(!isCancelled()) {  
    statusHandler.TaskRunning(this);
    super.run();
  }
}

还是应该打电话给我super.run()?在检查取消和对其进行任何操作之间,这两种方法似乎都容易受到比赛条件的影响。


阅读 246

收藏
2020-12-03

共1个答案

一尘不染

您说对了,那是对的。FutureTask#done()系统 最多 会调用 一次
,因此如果任务在通过运行之前已被取消RunnableFuture#run(),您将错过对的呼叫FutureTask#done()

您是否考虑过一种更简单的方法,该方法总是向ITaskStatusHandler#taskRunning()和发出对称的成对调用集ITaskStatusHandler#taskCompleted(),像这样?

@Override
public void run() {
  statusHandler.TaskRunning(this);
  try {
    super.run();
  finally {
    statusHandler.TaskCompleted(this);
  }
}

一旦RunnableFuture#run()被调用,您的任务确实正在运行,或者至少正在尝试运行。一旦FutureTask#run()完成,你的任务不再运行。碰巧在取消的情况下,过渡是(几乎)立即进行的。

试图避免ITaskStatusHandler#taskRunning()如果内部CallableRunnable从未被调用,FutureTask#run()则需要您在Callableor
Runnable和-
FutureTask派生类型本身之间建立一些共享结构,以便在首次调用内部函数时,设置一些标志,使外部FutureTask派生类型可以观察为锁存器,表明是的,该函数在取消
之前 确实开始运行。但是,到那时,您必须承诺要调用ITaskStatusHandler#taskRunning(),因此区别并不是那么有用。

我一直在挣扎了类似的设计问题最近,彼时安定在对称的 手术后 我重写FutureTask#run()方法。

2020-12-03