java - futures - Utilidad del método Future.cancel(boolean)
futures in java (5)
Simplemente estaba explorando el paquete java.util.concurrent.
Aprendí que la clase '' Future '' tiene un método boolean cancel (boolean mayInterruptIfRunning)
Adjunto el código de prueba que escribí:
package com.java.util.concurrent;
import java.util.concurrent.Callable;
import java.util.concurrent.FutureTask;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.ScheduledThreadPoolExecutor;
public class FutureTester {
/**
* @param args
* @throws InterruptedException
*/
public static void main(String[] args) throws InterruptedException {
// TODO Auto-generated method stub
int poolCnt = 1;
Callable<NumberPrinter> numberPrinter = null;
ScheduledThreadPoolExecutor schPool = new ScheduledThreadPoolExecutor(
poolCnt);
ScheduledFuture<NumberPrinter>[] numPrinterFutures = new ScheduledFuture[poolCnt];
FutureTask<NumberPrinter>[] futureTask = new FutureTask[poolCnt];
for (int i = 0; i < poolCnt; i++) {
numberPrinter = new NumberPrinter();
futureTask[i] = new FutureTask<NumberPrinter>(numberPrinter);
/*
* numPrinterFutures[i] = (ScheduledFuture<NumberPrinter>) schPool
* .schedule(futureTask[i], 0, TimeUnit.MILLISECONDS);
*/
numPrinterFutures[i] = (ScheduledFuture<NumberPrinter>) schPool
.submit(futureTask[i]);
}
//Thread.sleep(30);
if (numPrinterFutures.length > 0) {
System.out.println("Task completed ? "
+ numPrinterFutures[0].isDone());
System.out.println("Task cancelled ? "
+ numPrinterFutures[0].cancel(true));
System.out.println("Is task cancelled ? "
+ numPrinterFutures[0].isCancelled());
}
}
}
class NumberPrinter implements Callable<NumberPrinter> {
private int counter = 10;
@Override
public NumberPrinter call() throws Exception {
// TODO Auto-generated method stub
while (counter > 0) {
if (Thread.interrupted()) {/*OUCH !!!*/
return null;
}
System.out.println("counter = " + (counter--));
}
return this;
}
}
Inicialmente, asumí que cancelar una tarea también detendrá la ejecución de un hilo en ejecución ( la parte ''OUCH'' NO está incluida ). Pero obtuve el resultado de la siguiente manera:
counter = 10
Task completed ? false
counter = 9
Task cancelled ? true
counter = 8
Is task cancelled ? true
counter = 7
counter = 6
counter = 5
counter = 4
counter = 3
counter = 2
counter = 1
En una lectura adicional en stackoverflow, se dijo que
- El método ''cancelar'' solo puede detener los trabajos ''no iniciados'' (lo que contradice con la descripción de la API del método)
- El método cancelar simplemente interrumpe el hilo en ejecución que luego debe regresar desde el método run ()
Por lo tanto, incluí la parte ''OUCH'' - un ciclo while que verifica la interrupción , la salida fue la siguiente:
Task completed ? false
counter = 10
Task cancelled ? true
Is task cancelled ? true
PREGUNTA:
Si se supone que uno debe escribir algo análogo a la parte ''OUCH'' para detener el hilo en ejecución, ¿cuál es la utilidad / valor del método de cancelar? ¿Cómo ayuda la cancelación de un Callable en FutureTask si el Thread no se puede detener mediante la cancelación? ¿Cuál es la parte de diseño / conceptual / lógica que estoy pasando por alto?
¿Cómo ayuda la cancelación de un Callable en FutureTask si el Thread no se puede detener mediante la cancelación?
Desea cancelar la tarea, no el hilo que la ejecuta. El uso de cancelar (verdadero) impide que la tarea se inicie (pero no la elimina de la cola) e interrumpe el hilo si la tarea ha comenzado. La Tarea puede ignorar la interrupción, pero no hay una forma limpia de matar un hilo sin matar todo el proceso.
El problema que está pasando por alto es que solo los subprocesos que cooperan se pueden detener de forma segura en Java.
De hecho, si nos fijamos en la API Thread, notará que hay algunos métodos llamados destroy
, pause
, stop
y resume
que fueron desaprobados en Java 1.1. La razón por la que fueron desaprobados es que los diseñadores de Java se dieron cuenta de que generalmente no se pueden usar de manera segura. Las razones se explican en la nota "¿Por qué Thread.stop, Thread.suspend y Thread.resume están en desuso?" .
El problema es inherente al modelo de subprocesamiento de Java, y solo podría evitarse al restringir la capacidad de un subproceso de interactuar con los objetos utilizados por otros subprocesos. Hay un JSR que especifica una forma de hacerlo ... Se aísla ... pero ninguna JVM convencional implementa estas API, que yo sepa.
Entonces, volviendo a su pregunta, la utilidad de Future.cancel
es que resuelve el subconjunto del problema que puede resolverse en el contexto de futuros.
Hay un error en tu código. Debe verificar si un hilo actual está interrumpido o no,
if(Thread.currentThread().isInterrupted()) { /*OUCH !!!*/
return null;
}
De este modo, el hilo de ejecución se interrumpe y regresa del método de ejecución.
La invocación de cancel(true)
evitará que el futuro se ejecute si no se ha ejecutado y se interrupted
si se está ejecutando. En este punto, la carga de cancelar el Future
se pone al desarrollador.
Como se trata de un grupo de subprocesos, no tiene sentido detener el subproceso (aunque rara vez tiene sentido detener un subproceso). La cancelación / interrupción no hará que el hilo salga de su método de ejecución. Después de la ejecución del método de llamada de su Llamada, simplemente extraerá el siguiente elemento de la cola de trabajo y lo procesará.
La utilidad del método de cancelar es simplemente señalar el hilo de ejecución que algún proceso desea que se detenga el acceso invocable, no el hilo, por lo que tendrá que encargarse usted mismo de detener el invocable.
Supongamos que el código que se ejecuta como parte de su futuro no es compatible con la cancelación o interrupción cooperativa. Luego, cancelar una tarea no iniciada es lo mejor que puedes hacer. Es por eso que este método existe.
En general, pienso que la cancelación es estrictamente cooperativa. Solo cancelar un fragmento de código por la fuerza puede conducir a un estado dañado.