扩展FutureTask,如何处理取消

Ano*_*nym 5 java concurrency multithreading

我已经扩展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);
    }

}
Run Code Online (Sandbox Code Playgroud)

现在,我看到的是,如果任务被提交,但最终排队等待cancel(true);任务 - run()方法仍然被调用 - 并且FutureTask.run()(可能)检查任务被取消并且不调用被包装的可调用对象.

我应该这样做吗

@Override
public void run() {
  if(!isCancelled()) {  
    statusHandler.TaskRunning(this);
    super.run();
  }
}
Run Code Online (Sandbox Code Playgroud)

或者我还应该打电话super.run()?这两种方法在检查取消和做某事之间似乎容易受到竞争条件的影响......任何想法都会受到重视.

seh*_*seh 4

你说得对,那里有一场比赛。最多FutureTask#done()会被调用一次,因此如果任务在通过 运行之前已经被取消,那么您将错过对RunnableFuture#run()FutureTask#done()

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

@Override
public void run() {
  statusHandler.TaskRunning(this);
  try {
    super.run();
  finally {
    statusHandler.TaskCompleted(this);
  }
}
Run Code Online (Sandbox Code Playgroud)

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

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

我最近一直在努力解决类似的设计问题,并最终在我的重写方法中解决了操作前后的对称问题。FutureTask#run()