44 votes

Comment lancer une exception vérifiée à partir d'un thread java ?

J'écris une application réseau, dans laquelle je lis des paquets d'un format binaire personnalisé. Et je démarre un thread en arrière-plan pour attendre les données entrantes. Le problème est que le compilateur ne me permet pas de mettre du code lançant des exceptions (vérifiées) dans le fichier run() . C'est écrit :

run() in (...).Listener cannot implement run() in java.lang.Runnable; overridden method does not throw java.io.IOException

Je veux que l'exception tue le fil et qu'elle soit rattrapée quelque part dans le fil parent. Est-ce possible ou dois-je traiter chaque exception ? à l'intérieur de le fil ?

1 votes

Jetez un coup d'oeil à la réponse suivante : [Comment attraper une exception depuis un thread][1] [1] : stackoverflow.com/questions/6546193/

50voto

Esko Luontola Points 53877

Pour pouvoir envoyer l'exception au thread parent, vous pouvez placer votre thread d'arrière-plan dans un fichier Appelable (ce qui permet de lancer des exceptions également vérifiées) que vous passez ensuite à la fonction soumettre méthode de un certain exécuteur . La méthode submit renvoie un Futur que vous pouvez ensuite utiliser pour obtenir l'exception (son [obtenir](http://java.sun.com/javase/6/docs/api/java/util/concurrent/Future.html#get()) lancera un ExecutionException qui contient l'exception originale).

44voto

Grundlefleck Points 29486

Attention : cela peut ne pas répondre à vos besoins si vous devez utiliser le mécanisme d'exception.

Si je comprends bien, vous n'avez pas besoin que l'exception soit vérifiée (vous avez accepté la réponse suggérant une exception non vérifiée). Un simple modèle d'écouteur serait-il plus approprié ?

L'écouteur pourrait se trouver dans le fil d'exécution parent et, lorsque l'exception vérifiée est détectée dans le fil d'exécution enfant, il suffirait de notifier l'écouteur.

Cela signifie que vous avez un moyen d'exposer que cela se produira (par le biais de méthodes publiques), et que vous serez en mesure de transmettre plus d'informations qu'une exception ne le permet. Mais cela signifie qu'il y aura un couplage (bien que lâche) entre le thread parent et le thread enfant. Cela dépend de votre situation spécifique et de l'avantage qu'il y aurait à envelopper l'exception vérifiée avec une exception non vérifiée.

Voici un exemple simple (un peu de code emprunté à une autre réponse) :

public class ThingRunnable implements Runnable {
    private SomeListenerType listener;
    // assign listener somewhere

    public void run() {
        try {
            while(iHaveMorePackets()) { 
                doStuffWithPacket();
            }
        } catch(Exception e) {
            listener.notifyThatDarnedExceptionHappened(...);
        }
    }
 }

Le couplage provient du fait qu'un objet dans le thread parent doit être de type SomeListenerType .

0 votes

Bonne idée. Et oui, vous avez raison, je ne me soucie pas de savoir si l'exception est vérifiée ou non, je voulais juste un moyen simple de faire sortir une exception.

0 votes

Le code de l'écouteur ne s'exécute-t-il pas encore réellement dans le thread enfant ? Je comprends qu'il a maintenant trop de portée dans le thread parent... mais il s'exécute toujours dans le thread enfant.

1 votes

Il serait exécuté dans le fil enfant, mais il correspond à ce qui a été demandé, je pense.

37voto

a.b.d Points 1206

Cette réponse est basée sur celle d'Esko Luontola mais elle fournit un exemple de travail.

Contrairement à la méthode run() de l'interface Runnable, la méthode call() de Callable permet de lancer certaines exceptions. Voici un exemple d'implémentation :

public class MyTask implements Callable<Integer> {

    private int numerator;
    private int denominator;

    public MyTask(int n, int d) {
        this.numerator = n;
        this.denominator = d;
    }

    @Override
    // The call method may throw an exception
    public Integer call() throws Exception {
        Thread.sleep(1000);
        if (denominator == 0) {
            throw new Exception("cannot devide by zero");
        } else {
            return numerator / denominator;
        }
    }

}

L'exécuteur fournit un mécanisme permettant d'exécuter un Callable dans un thread et de gérer tout type d'exception :

public class Main {

    public static void main(String[] args) {

        // Build a task and an executor
        MyTask task = new MyTask(2, 0);
        ExecutorService threadExecutor = Executors.newSingleThreadExecutor();

        try {
            // Start task on another thread
            Future<Integer> futureResult = threadExecutor.submit(task);

            // While task is running you can do asynchronous operations
            System.out.println("Something that doesn't need the tasks result");

            // Now wait until the result is available
            int result = futureResult.get();
            System.out.println("The result is " + result);
        } catch (ExecutionException e) {
            // Handle the exception thrown by the child thread
            if (e.getMessage().contains("cannot devide by zero"))
                System.out.println("error in child thread caused by zero division");
        } catch (InterruptedException e) {
            // This exception is thrown if the child thread is interrupted.
            e.printStackTrace();
        }
    }
}

10 votes

Cela résout le problème des exceptions non saisies, mais pourrait introduire des problèmes propres. L'appel à get() bloquera jusqu'au retour de la tâche appelable. Cela signifie que vous n'obtenez plus aucun parallélisme de la part du thread d'arrière-plan. Pire encore, si la tâche est continue/longue (comme l'attente de paquets réseau dans une boucle), la fonction Callable sera jamais retourner et votre fil principal est bloqué de façon permanente. Je ne veux pas dire qu'il n'y a pas d'applications où ce modèle a un sens (je suis sûr qu'il y en a beaucoup), mais simplement que vous devez être prudent.

0 votes

Bon point. Dans un exemple réel, vous n'appelleriez pas get juste après submit .

0 votes

Dans un exemple réel, vous pourriez lancer N opérations d'arrière-plan en parallèle et attendre les résultats de M opérations d'arrière-plan où M <= N pour prendre une décision. Dans ce cas, vous appelleriez get juste après submit.

9voto

Don Branson Points 7100

Ce que je fais, c'est attraper l'exception dans le fil et la stocker comme une variable membre du Runnable. Cette exception est ensuite exposée via un getter sur le Runnable. J'analyse ensuite tous les threads du parent pour voir si l'un d'entre eux contient des exceptions, et je prends les mesures appropriées.

2 votes

Puis-je demander (au cas où ma réponse serait erronée), pourquoi stocker une variable comme un getter et la rechercher plutôt que d'utiliser un mécanisme d'écoute ?

0 votes

Bonne question. Les deux approches fonctionnent. Je pense que je vais essayer votre suggestion la prochaine fois, et voir si je préfère cela.

0 votes

Voir mon commentaire sur la réponse de Grundlefleck - je crois que cette solution permet de ramener la gestion des exceptions dans le fil d'exécution parent, alors que la solution de Grundlefleck ne le fait pas. (Celle de Grundlefleck corrige les problèmes de portée - mais celle-ci corrige des problèmes qui sont vraiment liés au contexte du thread).

3voto

rmn Points 1761

Le thread ne peut pas lancer l'exception à un autre thread (ni au thread principal). et vous ne pouvez pas faire en sorte que la méthode run() héritée lance des exceptions vérifiées puisque vous ne pouvez lancer que moins que le code hérité, pas plus.

0 votes

Bon point sur le fait qu'il n'est pas possible de "lancer" directement l'exception vers le thread parent.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X