2009-08-27 9 views
4

J'ai une petite application de traitement d'image qui fait plusieurs choses à la fois en utilisant SwingWorker. Cependant, si je cours le code suivant (extrait simplifié), il se bloque simplement sur JDK 7 b70 (windows) mais fonctionne en 6u16. Il démarre un nouveau worker dans un autre worker et attend son résultat (l'application réelle exécute plusieurs sous-workers et attend tout cela). Ai-je utilisé de mauvais modèles ici (comme il y a surtout 3-5 travailleurs dans la piscine de swingworker, qui a une limite de 10 je pense)?JDK-7 Deadlocks SwingWorker?

import javax.swing.SwingUtilities; 
import javax.swing.SwingWorker; 

public class Swing { 
     static SwingWorker<String, Void> getWorker2() { 
       return new SwingWorker<String, Void>() { 
         @Override 
         protected String doInBackground() throws Exception { 
           return "Hello World"; 
         } 
       }; 
     } 
     static void runWorker() { 
       SwingWorker<String, Void> worker 
        = new SwingWorker<String, Void>() { 
         @Override 
         protected String doInBackground() throws Exception { 
           SwingWorker<String, Void> sw2 = getWorker2(); 
           sw2.execute(); 
           return sw2.get(); 
         } 
       }; 
       worker.execute(); 
       try { 
         System.out.println(worker.get()); 
       } catch (Exception e) { 
         e.printStackTrace(); 
       } 
     } 
     public static void main(String[] args) { 
       SwingUtilities.invokeLater(new Runnable() { 
         @Override 
         public void run() { 
           runWorker(); 
         } 
       }); 
     } 

} 
+0

Avez-vous essayé d'obtenir un vidage de pile? –

+0

Il semble qu'il se bloque sur sw2.get() et il n'y a qu'un seul thread nommé par swingworker dans jdk7. Sur jdk6, je vois 3-5 à la fois. – akarnokd

Répondre

6

Comme personne n'a tiré hors le lien encore, il semble que ce soit en fait un bug connu:

http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6880336

Étonnamment, il y a moins de 100 votes pour ce qui devrait être un bug Showstopper pour la plupart non-trivial applications.

+0

Oui, et le programme exemple ressemble beaucoup à la mienne, n'est-ce pas? – akarnokd

0

En regardant le code source pour SwingWorker, il ressemble à un ExecutorService est utilisé comme un pool de threads de travail. Il est possible que le type de ExecutorService utilisé ait changé entre Java 6 et Java 7. Il semblerait que votre code soit bloqué si le ExecutorService ne gère qu'un seul thread à la fois (comme vous l'avez remarqué). C'est parce que votre appel 'sw2.get()' bloquera le thread courant, qui est le même thread que sw2 essayera d'utiliser. sw2 ne peut jamais s'exécuter car le premier opérateur bloque.

Je pense que la meilleure solution est de changer votre logique afin que vous n'appeliez pas des chaînes de travailleurs Swing comme ça. Vos SwingWorkers sont exécutés dans votre thread SwingWorker.

+0

Hmm, ça ne sonne pas un changement compatible - si j'ai plusieurs tours de travail en cours d'exécution, ils fonctionneront toujours en séquence sur JDK 7 (indépendamment d'un cpu multicœur)? Je suppose que je dois changer le swingworker externe dans un fil normal par exemple. – akarnokd

1

Donc, quand vous voyez

Il semble qu'il se bloque sur sw2.get() et il n'y a qu'un seul thread nommé par swingworker dans jdk7. Sur jdk6, je vois 3-5 à la fois. - kd304

En effet, la classe SwingWorker est pas un fil, mais une tâche à exécuter sur un fil, et la configuration par défaut du ExecutorService pour SwingWorker en Java 6 est configuré différent de celui en Java 7 IE Votre SwingWorkerExecutorService (qui est défini dans la classe SwingWorker) a une valeur différente pour le nombre maximal de Threads à allouer aux tâches.

//From Java 6 SwingWorker 

private static final int MAX_WORKER_THREADS = 10; 

public final void execute() { 
    getWorkersExecutorService().execute(this); 
} 

private static synchronized ExecutorService getWorkersExecutorService() { 
... 
private static synchronized ExecutorService getWorkersExecutorService() { 
new ThreadPoolExecutor(0, MAX_WORKER_THREADS, 
            1L, TimeUnit.SECONDS, 
            new LinkedBlockingQueue<Runnable>(), 
            threadFactory) 
} 

Vous avez seulement un thread exécutant les tâches SwingWorker, et que la première tâche attend la fin de la deuxième tâche, qui ne peut pas être exécuté parce que le fil de la deuxième tâche sera exécutée sur est attendre la fin de la deuxième tâche avant de revenir. La création d'un fil de travail dépendante de l'exécution d'un autre est un chemin sûr vers l'interblocage. Vous souhaiterez peut-être utiliser un ExecutorService pour planifier des événements à exécuter sur le thread SwingWorker et ne pas faire dépendre un événement planifié de l'achèvement d'un autre événement planifié.

Java 7 SwingWorker

+0

Explication juste, mais pourquoi le comportement a-t-il changé? Je m'attendrais à voir 3 threads swingworker démarrés dans JDK 7 aussi bien? Alors l'exécution originale de swignworker multi-thread était le bogue? – akarnokd

-1

Avant la mise à jour 18 JDK vous pouvez exécuter:

public static void main(String[] args) { 

    new SwingWorker<Void, Void>() { 
     @Override 
     protected Void doInBackground() throws Exception { 
      System.out.println("ok"); 
      return null; 
     } 
    }.execute(); 

} 

Ce code ne fonctionne plus, tout simplement parce que SwingWorkers doivent être exécutés sur EDT. Par conséquent, vous ne pouvez pas imbriquer SwingWorkers (sw2 ne fonctionnera jamais dans votre exemple de code dans les JDK les plus récents).

Je suppose que le remplacement de swingWorkers imbriqués avec les appels java.util.concurrent.Future d'executorService est une bonne solution de contournement.

+2

SwingWorkers ne doit pas nécessairement être exécuté sur l'EDT. En fait, le Javadoc de SwingWorker mentionne explicitement le «thread courant» dont est issu le SwingWorker, sans rien dire à la note qu'il doit s'agir du Thread Event Dispatch. – Trejkaz