views:

761

answers:

4

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();
                       }
               });
       }

}
A: 

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.

Outlaw Programmer
Hmm, it doesn't sound a compatible change - if I have multiple swingworkers executing, they will always run in sequence on JDK 7 (regardless of a multicore cpu)? I guess I have to change the outer swingworker into a normal Thread for example.
kd304
A: 

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

hasalottajava
Fair explanation, but why has the behavior changed? I would expect to see 3 swingworker- threads started in JDK 7 as well? Then the original multi-threaded swignworker execution was the bug?
kd304
A: 

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.

mimix
SwingWorkers don't necessarily have to be executed on the EDT. In fact, the SwingWorker Javadoc explicitly mentions the "current thread" which the SwingWorker is spawned from, not saying anything to the note that it must be the Event Dispatch Thread.
Trejkaz
+2  A: 

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.

Trejkaz