He extendido FutureTask
de java.util.concurrent
para proporcionar devoluciones de llamada para rastrear la ejecución de las tareas enviadas a ExecutorService
.Extendiendo FutureTask, cómo manejar cancelar
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);
}
}
Ahora, lo que veo es que si se presenta la tarea, pero termina en cola y me cancel(true);
la tarea - el método run()
todavía se llama - y la FutureTask.run()
(probable) comprueba que la tarea se cancela y doesn Llamar al envolvente llamable.
¿Debo hacerlo, p.
@Override
public void run() {
if(!isCancelled()) {
statusHandler.TaskRunning(this);
super.run();
}
}
¿O todavía debo llamar al super.run()
? Ambos enfoques parecen susceptibles a las condiciones de carrera entre verificar la cancelación y hacer algo al respecto ... cualquier idea apreciada.
Supongo que la llamada en la cláusula finally debe ser statusHandler.TaskCompleted (this); ¿Hay algún caso en el que no se invoque el método de ejecución, pero el método done() lo haría? – nos
Gracias por tomar el error. Solucioné la llamada en el bloque finally. Sí, es posible que done() se pueda invocar sin ejecutar run(). Vea FutureTask # SynC# innerCancel (boolean). Allí, siempre que la tarea no haya terminado de ejecutarse, lo que incluye que nunca ha comenzado a ejecutarse, puede ver que se llamará a done(). Tenga en cuenta que done() se llamará cero o una vez para cualquier instancia de FutureTask: cero si nunca se llama ni a run() ni a cancel(), de lo contrario. – seh
Parece que, si lo envía a un ejecutor, se llamará a done() si la tarea se cancela antes de que se ejecute, aunque el ejecutor no lo sabe. El ejecutor solo sabe sobre runnables/callables. Entonces, se llamará a run() cuando eventualmente se vuelva ejecutable, en ese caso FutureTask # run() esencialmente no hace nada. – nos