2012-03-02 14 views
29

Estaba simplemente explorando el paquete java.util.concurrent.Utilidad del método Future.cancel (boolean)

I aprendido que la clase 'futuro' tiene un método booleano cancelan (mayInterruptIfRunning booleano)

Se adjunta 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; 
} 

} 

Aunque al principio, supuse que cancelar una tarea también detendrá la ejecución de un hilo en ejecución (la parte 'OUCH' NO incluida) .Pero obtuve la salida 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 la lectura adicional en stackoverflow sí, se dijo que

  1. El 'cancelar' método sólo puede detener a los puestos de trabajo 'que no han empezado' (que se contradice con la descripción de la API del método)
  2. el método cancelar simplemente interrumpe el hilo conductor que luego debe volver desde el método run()

De ahí, que incluía la parte 'OUCH' - control de un bucle while para la interrupción, la salida fue el siguiente:

Task completed ? false 
counter = 10 
Task cancelled ? true 
Is task cancelled ? true 

PREGUNTA:

Si uno se supone que escribir algo análoga a la parte 'OUCH' para detener el hilo en ejecución, ¿cuál es la utilidad/valor del método 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?

+1

Me enfrenté al mismo problema. No sabía por qué mis grupos de hilos se están agotando en cierto punto. Ahora pensé por qué. Gracias. – tejas

Respuesta

19

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 de rosca, se dará cuenta de que hay algunos métodos llamados destroy, pause, stop y resume que quedaron en desuso 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 "Why are Thread.stop, Thread.suspend and Thread.resume Deprecated?".

El problema es inherente al modelo de subprocesamiento de Java, y solo podría evitarse al restringir la capacidad de un subproceso para 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.


Así llevando esto a su pregunta, la utilidad de Future.cancel es que resuelve el subconjunto del problema que puede ser resueltos en el contexto de los futuros.

7

La invocación de cancel(true) evitará que el futuro se ejecute si no se ha ejecutado ya y será interrupted si se está ejecutando actualmente. En este punto, la carga para cancelar el Future se pone al desarrollador.

Dado que es 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 indicar al hilo que se ejecuta que algún proceso desea que se cancele el bloqueo, no el hilo, por lo que tendrá que encargarse de detener el llamado invocable.

0

Supongamos que el código que se ejecuta como parte de su futuro no es compatible con la cancelación o la 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, creo que la cancelación es estrictamente cooperativa. Solo cancelar un fragmento de código por la fuerza puede conducir a un estado dañado.

24

¿Cómo ayuda el bloqueo de una llamada en FutureTask si el hilo no se puede detener al cancelar?

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.

-2

Hay un error en su código. Debe comprobar si un hilo de corriente se interrumpe o no el uso,

if(Thread.currentThread().isInterrupted()) { /*OUCH !!!*/ 
    return null; 
} 

De esta manera, el hilo de ejecución se interrumpe y vuelve a partir del método de ejecución.

+0

¿Por qué debería usarse 'Thread.currentThread(). IsInterrupted()' en lugar de 'Thread.interrupted()'? – Roman

+0

@Roman __Thread.currentThread(). IsInterrupted() __ no borra el estado interrumpido pero el último lo borra. –

+0

No entendí la pregunta. Eliminará la publicación. –