2009-05-27 9 views
2

J'ai 2 processus à exécuter dans mon application swing, un pour remplir une liste et un pour effectuer des opérations sur chaque élément de la liste. Je viens de déplacer les 2 processus dans les threads de Swingworker pour arrêter le blocage de l'interface graphique pendant que les tâches sont effectuées, et parce que je vais devoir faire cet ensemble d'opérations à plusieurs listes, donc la concurrence ne serait pas une mauvaise idée. endroit. Cependant, quand je viens de courirPlanification des threads Swingworker

fillList.execute();
doStuffToList.execute();

le thread doStuffToList à exécuter sur la liste vide (duh ...). Comment puis-je dire au deuxième processus d'attendre que le premier soit terminé? Je suppose que je pourrais juste imbriquer le deuxième processus à la fin de la première, mais je ne sais pas, cela semble être une mauvaise pratique.

+1

Des sons comme un BlockingQueue peuvent être utiles (http://java.sun.com/javase/6/docs/api/java/util/concurrent/BlockingQueue.html). –

Répondre

1

Quelque chose comme ça le ferait, je pense?

boolean listIsFull=false; 
class FillListWorker extends SwingWorker<Foo,Bar> 
{ 
    ... 
    protected void done() 
    { 
     synchronized (listYouveBeenFilling) 
     { 
      listIsFull=true; 
      listYouveBeenFilling.notifyAll(); 
     } 
    } 
    ... 
} 

class DoStuffToListListWorker extends SwingWorker<Foo,Bar> 
{ 
    ... 
    protected Foo doInBackground() 
    { 
     synchronized (listYouveBeenFilling) 
     { 
      while (!listIsFull) 
      { 
       try 
       { 
        listYouveBeenFilling.wait(); 
       } 
       catch (InterruptedException ie) 
       { 
        // Don't worry, we'll just wait again 
       } 
      } 
     } 
    } 
    ... 
} 
+0

J'aime beaucoup cette solution, mais ça ne marchera pas car 'listIsFull' doit être rendu final car on y accède depuis une classe interne ... ce qui rend impossible de modifier la valeur de plus tard vous essayez de le faire. – The111

+0

Il ne doit être «final» que si vous le définissez dans une méthode. Si vous le définissez comme un champ membre de la classe externe, tout ira bien. –

-1

Pour effectuer deux processus séquentiellement, vous appelez simplement une méthode après l'autre (!).

fillList(); 
doStuffToList(); 

Ou peut-être quelque chose comme:

doStuffToList(fillList()); 

Si vous traitez un à la fois, vous voudrez peut-être deux fils avec un BlockingQueue entre. Vous pourriez aller plus loin en ayant plusieurs fils de discussion.

En ce qui concerne le Thread AWT Event Dispatch Thread (EDT), il est simplement généré une action sans blocage, et sera notifié plus tard.

+0

Cela rate le point que SwingWorker # execute retourne immédiatement. L'OP sait clairement comment appeler une fonction après l'autre en général. –

1

Comment puis-je dire au deuxième processus d'attendre que le premier soit terminé? Je suppose que je pourrais juste imbriquer le deuxième processus à la fin de la première, mais je ne sais pas, cela semble être une mauvaise pratique.

Avez-vous envisagé d'utiliser des contrats à terme & à la place? Ils sonnent comme un bon match pour ce genre de chose (laisser le doStuffToList fonctionner sur un Future.get() au lieu de la liste réelle, donc il sera prêt quand get est appelé), en dehors de toute l'activité de swingworker .. (Considérez ceci une suggestion plutôt qu'une réponse)

+0

Cela pourrait être exactement ce que je veux, je regarde maintenant, merci – Simonw

0

Nous avons quelque chose comme ceci:

private SwingWorkerExecutor swingWorkerExecutor; 

//... 

protected void runChain(List<SwingWorker<Void>> chainWorkers, 
         final SwingWorkerExecutor.RunAfter<Void> runAfter, 
         final SwingWorkerExecutor.RunOnError runOnError) 
{ 
    final List<SwingWorker<Void>> remainingWorkers = 
     chainWorkers.subList(1, chainWorkers.size()); 
    SwingWorkerExecutor.RunAfter<Void> chainRunAfter; 
    if (chainWorkers.size() > 1) 
    { 
     chainRunAfter = new SwingWorkerExecutor.RunAfter<Void>() 
     { 
      @Override 
      public void run(Void value) 
      { 
       runChain(remainingWorkers, runAfter, runOnError); 
      } 
     }; 
    } 
    else 
    { 
     chainRunAfter = runAfter; 
    } 

    currentWorker = chainWorkers.get(0); 

    swingWorkerExecutor.execute(currentWorker, chainRunAfter, runOnError); 
} 

Ceci est assez simple, l'OMI, parce que dans notre cas, le SwingWorkerExecutor contient en fait tout le mal à comprendre des choses:

public class DefaultSwingWorkerExecutor implements SwingWorkerExecutor 
{ 
    @Override 
    public <T> void execute(SwingWorker<T, ?> worker, RunAfter<T> after, 
          RunOnError onError) 
    { 
     worker.addPropertyChangeListener(
      new RunAfterHandler<T>(worker, after, onError)); 
     worker.execute(); 
    } 

    private static class RunAfterHandler<T> implements PropertyChangeListener 
    { 
     private final SwingWorker<T, ?> worker; 
     private final RunAfter<T> after; 
     private final RunAfter<Throwable> onError; 

     protected RunAfterHandler(SwingWorker<T, ?> worker, RunAfter<T> after, 
            RunOnError onError) 
     { 
      this.worker = worker; 
      this.after = after; 
      this.onError = onError; 
     } 

     @Override 
     public void propertyChange(PropertyChangeEvent evt) 
     { 
      if ("state".equals(evt.getPropertyName()) && 
       evt.getNewValue() == SwingWorker.StateValue.DONE) 
      { 
       if (worker.isCancelled()) 
       { 
        return; 
       } 

       try 
       { 
        after.run(worker.get()); 
       } 
       catch (InterruptedException e) 
       { 
        Thread.currentThread().interrupt(); 
       } 
       catch (ExecutionException e) 
       { 
        onError.run(e); 
       } 
      } 
     } 
    } 
} 

Il existe des interfaces manquantes Il devrait être assez simple d'écrire sans les voir ici. Notre véritable déploiement SwingWorkerExecutor s'exécute en utilisant un ExecutorService injecté au lieu de celui par défaut (cela réduit le nombre de pools de threads dont nous avons besoin pour une seule application). Mais la vraie raison pour laquelle nous avons introduit SwingWorkerExecutor était qu'il simplifiait et standardisait la gestion des SwingWorker conditions de succès et d'erreur et permet également de remplacer la logique pour les tests unitaires (qui, comme je suis sûr que vous le savez, sont beaucoup plus simples si elles sont à un seul thread.Comme vous pouvez le voir, il y a un tas de messages standards dont vous auriez normalement besoin pour chaque SwingWorker à l'intérieur de done(), donc au lieu de cela, nous déplaçons le travail done() dans un callback.

L'avantage secondaire est que l'exécution de plusieurs travailleurs Swing dans une chaîne devient très facile à mettre en œuvre.