问题
I have a small image processing application which does multiple things at once using SwingWorker. However, if I run the following code (oversimplified excerpt), it just hangs on JDK 7 b70 (windows) but works in 6u16. It starts a new worker within another worker and waits for its result (the real app runs multiple sub-workers and waits for all this way). Did I use some wrong patterns here (as mostly there is 3-5 workers in the swingworker-pool, which has limit of 10 I think)?
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();
}
});
}
}
回答1:
As nobody has fired off the link yet, it seems this is actually a known bug:
http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6880336
Surprisingly there are less than 100 votes for what should be a showstopper bug for most non-trivial applications.
回答2:
Your SwingWorkers are executed in your SwingWorker Thread. So when you see
It seems it hangs on sw2.get() and there is only one swingworker- named thread in jdk7. On jdk6, I see 3-5 at once. – kd304
This is because the SwingWorker class is not a thread, but a task to be run on a thread, and the default configuration for the ExecutorService for SwingWorker in Java 6 is configured different from the one in Java 7. IE your SwingWorkerExecutorService (which is defined inside the SwingWorker class) has a different value for the max number of Threads to allocate to the tasks.
//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)
}
You only have the one thread running the SwingWorker tasks, and that first task is waiting for the completion of the second task, which can't be run, because the Thread the second task would be run on is waiting for the second task to complete before it will return. Making on swingworker thread dependent on the execution of another is a sure path to deadlock. You may want to look at using an ExecutorService to schedule events to be run on the SwingWorker Thread, and don't make one scheduled event dependent on another scheduled event's completion.
Java 7 SwingWorker
回答3:
Looking at the source code for SwingWorker, it looks like an ExecutorService is being used as a pool of worker threads. It's possible that the type of ExecutorService used has changed between Java 6 and Java 7. It looks like your code will deadlock if the ExecutorService only manages exactly 1 thread at a time (as you seem to have noticed).
This is because your 'sw2.get()' call will block the current thread, which is the same thread the sw2 will try to use. sw2 can never execute because the first worker is blocking.
I think the best solution is to change your logic so that you don't call chains of Swing workers like this.
回答4:
Before JDK update 18 you could run:
public static void main(String[] args) {
new SwingWorker<Void, Void>() {
@Override
protected Void doInBackground() throws Exception {
System.out.println("ok");
return null;
}
}.execute();
}
This code doesn't work anymore, simply because SwingWorkers must be executed on EDT.
Therefore, you can't nest SwingWorkers (sw2 will never run in you sample code in newer JDKs).
I guess replacing nested swingWorkers with executorService java.util.concurrent.Future calls is a good workaround.
来源:https://stackoverflow.com/questions/1342294/jdk-7-swingworker-deadlocks