我已从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()
吗?这两种方法在检查取消和做某事之间似乎容易受到竞争条件的影响......任何想法都会受到赞赏。
答案 0 :(得分:4)
你说那里有比赛是对的。 FutureTask#done()
最多只会被称为 ,所以如果任务在通过RunnableFuture#run()
运行之前已被取消,那么您将错过对{{1}的调用}}
您是否考虑过一种更简单的方法,始终向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()
或Callable
,则要求您在Runnable
之间建立一些共享结构,以避免调用FutureTask#run()
或者Callable
和Runnable
- 派生类型本身,这样当你的内部函数首次被调用时,你设置一些标志,外部FutureTask
- 派生类型可以观察为一个锁存器,表明是的,该功能确实在被取消之前开始运行。但是,到那时,你必须致力于调用FutureTask
,所以这种区别并没有那么有用。
我最近一直在努力解决类似的设计问题,并在我的重写ITaskStatusHandler#taskRunning()
方法中解决了之前的对称和之后的操作。
答案 1 :(得分:2)
你的问题是你的未来任务在你取消之后仍然执行,对吗?
将任务提交给执行程序服务后,它应由执行程序管理。 (如果您愿意,您仍然可以取消单个任务。)您应该使用executor shutdownNow method取消执行。 (这将调用所有提交任务的cancel方法。)关闭仍将执行所有提交的任务。
执行人员不会“知道”任务被取消。它将独立于未来任务的内部状态调用该方法。
最简单的方法是按原样使用Executor框架并编写一个Callable装饰器。
class CallableDecorator{
CallableDecorator(Decorated decorated){
...
}
setTask(FutureTask task){
statusHandler.taskCreated(task);
}
void call(){
try{
statusHandler.taskRunning(task);
decorated.call();
}finally{
statusHandler.taskCompleted(task);
}
}
}
唯一的问题是任务不能在装饰器的构造函数中。 (它是未来任务构造函数的参数。)要打破此循环,您必须使用setter或某些代理来解决构造函数注入问题。也许这根本不需要回调,您可以说:statusHandler.callableStarted(decorated)
。
根据您的要求,您可能需要发出异常和中断信号。
基本实施:
class CallableDecorator<T> implements Callable<T> {
private final Callable<T> decorated;
CallableDecorator(Callable<T> decorated){
this.decorated = decorated;
}
@Override public T call() throws Exception {
out.println("before " + currentThread());
try {
return decorated.call();
}catch(InterruptedException e){
out.println("interupted " + currentThread());
throw e;
}
finally {
out.println("after " + currentThread());
}
}
}
ExecutorService executor = newFixedThreadPool(1);
Future<Long> normal = executor.submit(new CallableDecorator<Long>(
new Callable<Long>() {
@Override
public Long call() throws Exception {
return System.currentTimeMillis();
}
}));
out.println(normal.get());
Future<Long> blocking = executor.submit(new CallableDecorator<Long>(
new Callable<Long>() {
@Override
public Long call() throws Exception {
sleep(MINUTES.toMillis(2)); // blocking call
return null;
}
}));
sleep(SECONDS.toMillis(1));
blocking.cancel(true); // or executor.shutdownNow();
输出:
before Thread[pool-1-thread-1,5,main]
after Thread[pool-1-thread-1,5,main]
1259347519104
before Thread[pool-1-thread-1,5,main]
interupted Thread[pool-1-thread-1,5,main]
after Thread[pool-1-thread-1,5,main]