web-dev-qa-db-fra.com

Utilité de la méthode Future.cancel (booléenne)

J'explorais simplement le package Java.util.concurrent.

J'ai appris que la classe 'Future' a une méthode boolean cancel (boolean mayInterruptIfRunning)

Veuillez trouver ci-joint le code de test que j'ai écrit:

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

}

Au départ, je supposais que l'annulation d'une tâche arrêterait également l'exécution d'un thread en cours d'exécution ( la partie 'OUCH' NON incluse ) .Mais j'ai obtenu la sortie comme suit:

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

À la lecture de stackoverflow lui-même, il a été dit que

  1. La méthode 'cancel' ne peut que stopper les jobs 'non démarrés' (ce qui contredit la description api de la méthode)
  2. La méthode cancel interrompt simplement le thread en cours d'exécution qui doit ensuite revenir de la méthode run ()

Par conséquent, j'ai inclus la partie 'OUCH' - une boucle while vérifiant l'interruption; la sortie était la suivante:

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

QUESTION:

Si l'on est censé écrire quelque chose d'analogue à la partie 'OUCH' pour arrêter le thread en cours d'exécution, quelle est l'utilité/la valeur de la méthode d'annulation. En quoi le wrapping d'un Callable dans une FutureTask aide-t-il si le thread ne peut pas être arrêté par annuler? Quelle est la partie design/conceptuelle/logique que je néglige?

37

Le problème que vous ignorez est que seuls les threads coopérants peuvent être arrêtés en toute sécurité en Java.

En effet, si vous regardez l'API Thread, vous remarquerez qu'il existe des méthodes appelées destroy, pause, stop et resume qui étaient obsolètes in Java 1.1. La raison pour laquelle ils ont été dépréciés est que les concepteurs Java ont réalisé qu'ils ne peuvent généralement pas être utilisés en toute sécurité. Les raisons sont expliquées dans le note "Pourquoi Thread.stop, Thread.suspend et Thread.resume sont-ils obsolètes?" .

Le problème est inhérent au modèle de thread Java Java, et ne peut être évité qu'en réduisant la capacité d'un thread à interagir avec des objets utilisés par d'autres threads. Il existe un JSR qui spécifie une manière unique de faire ceci ... Isoler ... mais aucune machine virtuelle Java traditionnelle n'implémente ces API à ma connaissance.


Donc, ce qui ramène à votre question, l'utilité de Future.cancel est qu'il résout le sous-ensemble du problème que peut être résolu dans le contexte des futurs.

26
Stephen C

En quoi le wrapping d'un Callable dans une FutureTask aide-t-il si le thread ne peut pas être arrêté par annuler?

Vous souhaitez annuler la tâche, pas le thread qui l'exécute. L'utilisation d'annulation (true) empêche le démarrage de la tâche (mais ne la supprime pas de la file d'attente) et interrompt le thread si la tâche a démarré. La tâche peut ignorer l'interruption, mais il n'existe aucun moyen propre de tuer un thread sans tuer tout le processus.

29
Peter Lawrey

L'appel de cancel(true) empêchera le Future de s'exécuter s'il n'est pas déjà exécuté et sera interrupted s'il est en cours d'exécution. À ce stade, la charge d'annuler le Future incombe au développeur.

Puisqu'il s'agit d'un pool de threads, il ne serait pas logique d'arrêter le thread (bien qu'il soit rarement, voire jamais, logique d'arrêter un thread). L'annulation/interruption n'entraînera pas la sortie du thread de sa méthode d'exécution. Après l'exécution de la méthode d'appel de votre Callable, il retirera simplement l'élément suivant de la file d'attente de travail et le traitera.

L'utilité de la méthode d'annulation est simplement de signaler au thread d'exécution que certains processus veulent que Callable s'arrête - pas le thread - vous devrez donc gérer vous-même l'arrêt de Callable.

9
John Vint

Supposons que le code qui s'exécute dans le cadre de votre avenir ne prend pas en charge l'annulation ou l'interruption coopérative. Ensuite, l'annulation d'une tâche non démarrée est la meilleure chose que vous puissiez faire. C'est pourquoi cette méthode existe.

En général, je pense que l'annulation est strictement coopérative. L'annulation forcée d'un morceau de code peut conduire à un état corrompu.

0
usr