deadlock JDK-7 SwingWorker?
-
20-09-2019 - |
Domanda
Ho una piccola applicazione di elaborazione delle immagini che fa più cose in una volta usando SwingWorker. Tuttavia, se corro il seguente codice (estratto semplificato), appende appena il JDK 7 B70 (finestre), ma lavora in 6u16. Si inizia un nuovo lavoratore all'interno di un altro lavoratore e attende il suo risultato (la vera applicazione esegue più sotto-lavoratori e attende per tutti in questo modo). Ho usato alcuni modelli sbagliati qui (come in gran parte v'è di 3-5 lavoratori della SwingWorker-piscina, che ha limite del 10 credo)?
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();
}
});
}
}
Soluzione
Come nessuno ha sparato fuori il link eppure, sembra che questo è in realtà un bug noto:
http://bugs.sun.com/bugdatabase/view_bug.do? bug_id = 6880336
Sorprendentemente ci sono meno di 100 voti per quello che dovrebbe essere un bug showstopper per la maggior parte delle applicazioni non banali.
Altri suggerimenti
I tuoi SwingWorkers vengono eseguiti nel tuo thread SwingWorker. Quindi, quando si vede
Sembra che pende sulla sw2.get () e non v'è un solo swingworker- chiamato thread nel jdk7. Su JDK6, vedo 3-5 in una sola volta. - kd304
Questo è perché la classe SwingWorker non è un filo, ma un compito da eseguire su un thread, e la configurazione di default per l'ExecutorService per SwingWorker in Java 6 è configurato diverso da quello in Java 7. IE tua SwingWorkerExecutorService ( che è definito all'interno della classe SwingWorker) ha un valore differente per il numero massimo di thread da allocare ai compiti.
//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)
}
Hai solo l'un thread in esecuzione dei compiti SwingWorker, e che il primo compito è in attesa del completamento del secondo compito, che non può essere eseguito, perché il filo il secondo compito sarebbe stato eseguito su è in attesa per la seconda compito di completare prima che tornerà. Rendendo il filo SwingWorker dipende l'esecuzione di un altro è un percorso sicuro di deadlock. Si consiglia di guardare con un ExecutorService di pianificare eventi da eseguire sul filo SwingWorker, e non fare un evento in programma dipende dalla conclusione di un altro evento pianificato.
Guardando il codice sorgente per SwingWorker, sembra un ExecutorService viene utilizzato come un pool di thread di lavoro. E 'possibile che il tipo di ExecutorService utilizzato è cambiato tra il Java 6 e Java 7. Sembra che il codice verrà deadlock se l'ExecutorService riesce solo esattamente 1 thread alla volta (come ti sembra di avere notato).
Questo perché il 'sw2.get ()' chiamata bloccherà il thread corrente, che è lo stesso filo SW2 cercheranno di usare. SW2 non può mai eseguito perché il primo operaio sta bloccando.
Credo che la soluzione migliore è quella di cambiare la logica in modo che non si chiama catene di lavoratori swing come questo.
Prima di aggiornamento JDK 18 è possibile eseguire:
public static void main(String[] args) {
new SwingWorker<Void, Void>() {
@Override
protected Void doInBackground() throws Exception {
System.out.println("ok");
return null;
}
}.execute();
}
Questo codice non funziona più, semplicemente perché SwingWorkers devono essere eseguite sul EDT.
Di conseguenza, non è possibile annidare SwingWorkers (SW2 sarete mai a corto in voi codice di esempio in JDK più recenti).
Credo che la sostituzione swingWorkers nidificate con ExecutorService java.util.concurrent.Future chiama è una buona soluzione.