2008-11-16 9 views
4

Je travaille sur un projet J2ME qui génère des threads de travail pour de nombreuses tâches telles que le téléchargement de contenu HTTP. La disposition des threads de base est similaire à celle de la plupart des applications Java. Il existe un fil d'interface utilisateur principal et des threads de travail générés pour effectuer des tâches en arrière-plan. Ma question est quelle est la meilleure façon de gérer les exceptions qui se produisent dans les threads de travail? En général, j'adhère à la logique de conception selon laquelle la plupart des exceptions devraient être percolées autant que possible. Lorsque j'écris des applications à un seul thread, il est courant pour moi de filtrer les exceptions jusqu'à la couche d'interface utilisateur, puis de les signaler dans une boîte de dialogue d'erreur à l'utilisateur. Existe-t-il une pratique similaire pour les applications multithread? La chose la plus intuitive pour moi est d'attraper des exceptions dans le Thread.run(), puis d'appeler invokeLater sur le thread d'interface utilisateur pour le signaler dans une boîte de dialogue. Le problème que je vois ici est que, en dehors du thread de travail qui s'éteint prématurément, cette approche ne notifie pas vraiment le thread de l'interface utilisateur qu'il y avait une erreur. Je ne vois pas de moyen clair de lancer une exception à travers les threads pour ainsi dire.Quelle est la meilleure approche pour gérer les exceptions lancées dans un thread séparé?

Merci, Andy

Répondre

7

Vous ne devriez pas confiture code de l'interface dans vos employés!

/** 
* TWO CHOICES: 
* - Monitor your threads and report errors, 
* - setup a callback to do something. 
*/ 
public class ThreadExceptions { 

    /** Demo of {@link RunnableCatch} */ 
    public static void main(String[] argv) throws InterruptedException { 
     final Runnable bad = new NaughtyThread(); 
     // safe1 doesnt have a callback 
     final RunnableCatch safe1 = new RunnableCatch(bad); 
     // safe2 DOES have a callback 
     final RunnableCatch safe2 = new RunnableCatch(bad, new RunnableCallback() { 
      public void handleException(Runnable runnable, Exception exception) { 
       System.out.println("Callback handled: " + exception.getMessage()); 
       exception.printStackTrace(); 
      } 

     }); 
     final Thread t1 = new Thread(safe1, "myThread"); 
     final Thread t2 = new Thread(safe2, "myThread"); 
     t1.start(); 
     t2.start(); 
     t1.join(); 
     t2.join(); 
     if (safe1.getException() != null) { 
      System.out.println("thread finished with exceptions"); 
      safe1.getException().printStackTrace(); 
     } 
     System.out.println("done"); 
    } 


} 

/** Throws an exception 50% of the time */ 
class NaughtyThread implements Runnable { 
    public void run() { 
     try { 
      if (Math.random() > .5) { 
       throw new RuntimeException("badness"); 
      } 
     } finally { 
      System.out.println("ran"); 
     } 
    } 
} 

/** Called when an exception occurs */ 
interface RunnableCallback { 
    void handleException(Runnable runnable, Exception exception); 
} 

/** 
* Catches exceptions thrown by a Runnable, 
* so you can check/view them later and/or 
* deal with them from some callback. 
*/ 
class RunnableCatch implements Runnable { 

    /** Proxy we will run */ 
    private final Runnable _proxy; 

    /** Callback, if any */ 
    private final RunnableCallback _callback; 

    /** @guarded-by(this) */ 
    private Exception _exception; 

    public RunnableCatch(final Runnable proxy) { 
     this(proxy, null); 
    } 

    public RunnableCatch(final Runnable proxy, RunnableCallback target) { 
     _proxy = proxy; 
     _callback = target; 
    } 

    public void run() { 
     try { 
      _proxy.run(); 
     } catch (Exception e) { 
      synchronized (this) { 
       _exception = e; 
      } 
      if (_callback != null) { 
       _callback.handleException(_proxy, e); 
      } 
     } 
    } 

    /** @return any exception that occured, or NULL */ 
    public synchronized Exception getException() { 
     return _exception; 
    } 
} 
+0

Merci Stuph. Cela a plus de sens. Une question rapide sur votre exemple: le bloc synchronisé (this) est-il vraiment nécessaire autour de l'affectation à _exception? J'avais l'impression que les affectations d'objets étaient atomiques. – Andy

0

Une autre option que celle fournie par Stuph consiste à définir des exceptions dans le thread local. Si une autre exception se produit avant que cette exception ne soit effacée, une affirmation se produit. Cela donne au moins à quelqu'un l'occasion de remarquer l'exception et de la traiter.

Questions connexes