1 2 Previous Next 22 Replies Latest reply: Jan 28, 2010 7:45 AM by 800330 RSS

    SwingWorker with JDK update 18

    843807
      Hello.

      I have used SwingWorkers with the previous versions of the JDK. However, with update 18 I get a different behaviour. I start a SwingWorker and inside this SwingWorker I start a second one. Then I wait for the second one having finished. However, this is an endless loop with JDK update 18! Can someone please help me to understand why the program behaves different with update 18?

      Here is the code made as simple as possible:
      SwingWorker<Integer, Integer> sw1 = new SwingWorker<Integer, Integer>() {
      
           @Override
           protected Integer doInBackground() throws Exception {
                               
                SwingWorker<Integer, Integer> sw2 = new SwingWorker<Integer, Integer>() {
                     @Override
                     protected Integer doInBackground() throws Exception {
                          System.out.printf("\nsw2 executed");
                          return new Integer(1);
                     }
                };
                sw2.execute();
                               
                System.out.printf("\nbefore get");
                sw2.get();
                System.out.printf("\nafter get");
                     
                return new Integer(1);
           }
      };
      sw1.execute();
      Thanks!

      Edited by: MatthiasMeger on 18.01.2010 22:09
        • 1. Re: SwingWorker with JDK update 18
          843807
          MatthiasMeger wrote:
          I have used SwingWorkers with the previous versions of the JDK. However, with update 18 I get a different behaviour. I start a SwingWorker and inside this SwingWorker I start a second one.
          This makes me curious. Why call a SwingWorker in a non-EDT thread?
          Then I wait for the second one having finished. However, this is an endless loop with JDK update 18! Can someone please help me to understand why the program behaves different with update 18?
          Here is the code made as simple as possible:
          ....
          Perhaps too simple. You should look into creating and posting an SSCCE (please read the link for details).

          Much luck!
          • 2. Re: SwingWorker with JDK update 18
            843807
            I call a SwingWorker in a non EDT-Thread because the called method should run in background but publish process information in a Swing Component which should be done in the EDT. And this is exactly what SwingWorkers are useful for. It doesn't matter whether the SwingWorker is created in the EDT or another thread.

            I thought I had already posted a SSCCE. However, here is a coding that just can be saved to Test.java and then be compiled using javac:
            public class Test {
            
                 static public void main(String[] argv) {
            
                      javax.swing.SwingWorker<Integer, Integer> sw1 = new javax.swing.SwingWorker<Integer, Integer>() {
            
                           protected Integer doInBackground() throws Exception {
                                javax.swing.SwingWorker<Integer, Integer> sw2 = new javax.swing.SwingWorker<Integer, Integer>() {
                                     protected Integer doInBackground() throws Exception {
                                          System.out.printf("\nsw2 executed");                                   
                                          return new Integer(1);
                                     }                    
                                };
                                sw2.execute();
            
                                System.out.printf("\nbefore get");
                                sw2.get();
                                System.out.printf("\nafter get");
            
                                return new Integer(1);
                           }          
                      };
                      sw1.execute();
                      
                      try {
                           Thread.sleep(10000);
                      } catch (InterruptedException e) {}
                 }
            }
            Can anybody please test this and confirm that the coding does the following:
            On JDK 1.6 update 17 and older: output is:
            before get
            sw2 executed
            after get

            and using JDK 1.6 udpate 18 the output is:
            before get

            Thanks!

            Edited by: MatthiasMeger on 19.01.2010 17:34
            • 3. This Thread is now moved
              DarrylBurke
              Note: This thread was originally posted in the [Java Programming|http://forums.sun.com/forum.jspa?forumID=31] forum, but moved to this forum for closer topic alignment.
              • 4. Re: SwingWorker with JDK update 18
                843807
                I can verify that behavior. And I think I know what's going on to. In java 1.6.0_18 the SwingWorker class is using the following ThreadPoolExecutor to run the doInBackground methods.
                executorService = 
                        new ThreadPoolExecutor(1, MAX_WORKER_THREADS,
                                               10L, TimeUnit.MINUTES,
                                               new LinkedBlockingQueue<Runnable>(),
                                               threadFactory);
                That's a core pool size of 1, a max pool size of 10, and and unbounded queue.

                Now according to the java docs the ThreadPoolExecutor class behaves like so,
                If fewer than corePoolSize threads are running, the Executor always prefers adding a new thread rather than queuing.

                If corePoolSize or more threads are running, the Executor always prefers queuing a request rather than adding a new thread.

                If a request cannot be queued, a new thread is created unless this would exceed maximumPoolSize, in which case, the task will be rejected.
                So what happens is sw1 gets executed on a background thread. Then sw2 is requested. Because the number of running threads is >= the corePoolSize, the Executor queues sw2. This never fails since an unbounded queue is used. Then when the background thread waits for sw2 to complete a deadlock occurs. It can't complete because it's the very same background thread that has to run sw2.

                It's also interesting to note that by using an unbounded queue, the maxiumPoolSize is effectively ignored. In fact, this is documented.
                Using an unbounded queue (for example a LinkedBlockingQueue without a predefined capacity) will cause new tasks to wait in the queue when all corePoolSize threads are busy. Thus, no more than corePoolSize threads will ever be created. (And the value of the maximumPoolSize therefore doesn't have any effect.) This may be appropriate when each task is completely independent of others, so tasks cannot affect each others execution; for example, in a web page server.
                So declaring MAX_WORKER_THREADS as a class variable becomes pointless. And now all of a sudden all SwingWorkers necessarily run on one thread.
                • 5. Re: This Thread is now moved
                  843807
                  Maxidion, thanks for your reply. So what is your solution? to not use two SwingWorker threads at one time? To make sure that one is a plain-vanilla background thread?
                  • 6. Re: This Thread is now moved
                    843807
                    Actually, I would consider the behavior a regression since SwingWorker tasks now have to be independent. Only one thread is executing them and hence that thread can't wait on another SwingWorker to complete. It's probably an oversight, I don't know.
                    • 7. Re: This Thread is now moved
                      jduprez
                      Thanks for your nice analysis Max (no first name/last name info on your profile page, so I have to wild-guess :o)
                      Actually, I would consider the behavior a regression since SwingWorker tasks now have to be independent. Only one thread is executing them and hence that thread can't wait on another SwingWorker to complete. It's probably an oversight, I don't know.
                      Definitely a bug to me.
                      Indeed it's already listed in the bug parade: [6880336|http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6880336] (not yet evaluated).
                      • 8. Re: SwingWorker with JDK update 18
                        800330
                        Notwithstanding regression and limited usefulness of deadlocks, how about the following:

                        A SwingWorker is said to have doInBackground() executed on a background thread (Worker thread) and invoking a get method will block the calling thread until the SW is done. The only claim in the documentation regarding the Worker thread is that it not the Event-Dispatch-Thread. So blocking your current thread by calling get() from code executing in a (or the!) Worker thread may be considered dangerous, no matter what pooling or queueing mechanism lies behind it. There is no guarantee (yet) that two SwingWorkers will execute on separate threads at all.

                        If it were documented that sw.execute() will execute doInBackground() on a Worker thread unequal to the Current thread, then your code is guaranteed to work. Other combinations of SWs that have all been started on the EDT and then block on get() calls to each other may still cause trouble, as calling get() from doInBackground assumes that the other SW is executing on a separate thread, which is not guaranteed at all.
                        • 9. Re: SwingWorker with JDK update 18
                          843807
                          Thanks for all your answers.
                          I consider this a bug now. I have created my own ExecutorService for executing the second SwingWorker as workaround as long as this is not fixed.
                          • 10. Re: SwingWorker with JDK update 18
                            843807
                            Hi, I just want to verify this bug as well. I will also try to demonstrate a possible scenario to execute a worker inside another worker. Consider this:
                            I have a GUI form that displays some person's data. On save I want first to verify that the person's Social Security Number is unique, so to avoid any unique field exceptions from the db. So I have implemented a ChainTask that "chains" together two SwingWorkers. The first checks SSN uniqueness and the second one does the actual save in db. If the first task in chain finishes successfully and report that SSN is unique, then I proceed with the second task execution. Sample code for the ChainTask's doInBackground() is:
                                protected Void doInBackground()
                                    throws Exception
                                {
                                    int count = 0;
                                    for(AbstractSwingWorker work : _jobs)
                                    {
                                        // execute next task in line.
                                        work.execute();
                            
                                        // loop until current task finishes (due to any reason: normal, cancelled, exception).
                                        do
                                        {
                                            // stay here for a while...
                                            Thread.sleep(50);
                            
                                            // if chain is cancelled, also cancel the current task.
                                            if( isCancelled() )
                                            {
                                                work.cancel(MAY_INTERRUPT);
                                            }
                            
                                            // stay here for a while...
                                            Thread.sleep(50);
                                        } while( !work.isDone() );
                            
                                        // since the current task if finished, try to fetch it's results,
                                        // in order to determine if there was an exception. In that case
                                        // we should abort further chain execution.
                                        try
                                        {
                                            work.get();
                                        }
                                        catch(Exception ignored)
                                        {
                                            work.continueWithNextTaskInChain = false;
                                        }
                            
                                        // if current task is cancelled or it requested to stop the chain,
                                        // (e.g. validation task), then cancel the chain.
                                        if( work.isCancelled() || !work.continueWithNextTaskInChain )
                                        {
                                            if( !isCancelled() )
                                            {
                                                cancel(MAY_INTERRUPT);
                                            }
                                        }
                            
                                        // if chain is cancelled, abort.
                                        if( isCancelled() )
                                        {
                                            break;
                                        }
                            
                                        // increase progress.
                                        count++;
                                        setProgress( (count*100)/_jobs.size() );
                                    }
                                    return( null );
                                }
                            This worked perfectly in JDK 1.6.0_u17, but with the new update 18 the code brakes. It actually loops forever inside the do/while after the first task in chain starts it's execution.
                            MatthiasMeger, I would like a sample code, if possible, of your workaround.
                            Regards
                            Greg--
                            • 11. Re: SwingWorker with JDK update 18
                              843807
                              My workaround is to use an own executor. Instead of:
                              SwingWorker sw1 = new SwingWorker() 
                              SwingWorker sw2 = new SwingWorker() 
                              sw1.execute()
                              sw2.execute()
                              I now have:
                              ExecutorService executor = Executors.newCachedThreadPool();
                              SwingWorker sw1 = new SwingWorker() 
                              SwingWorker sw2 = new SwingWorker() 
                              executor.execute(sw1);
                              executor.execute(sw2);
                              • 12. Re: SwingWorker with JDK update 18
                                843807
                                Thanks, it works now; although I am not quite sure there won't be any problems with more complicated use cases...
                                • 13. Re: SwingWorker with JDK update 18
                                  800330
                                  Greg,

                                  As it seems from your earlier posts, a set of SwingWorkers are never to execute their doInBackGround() method simultaneously when controlled by a ChainedTasks. Is it correct that you'd like them to execute one after the other in the order you supplied them?

                                  If so, wouldn't SwingWorker's done() method be your entry point of choice? That is called on the EDT, when the worker is finished. I'd code the chaining tasks like this:
                                   abstract class ChainingWorker extends SwingWorker {
                                     SwingWorker next;
                                  
                                     ChainingWorker setNext(SwingWorker next) {
                                        this.next = next;
                                        return this;
                                     }
                                  
                                    public void done(){
                                        if (!isCancelled()) {
                                            try {
                                                 if (next != null) {
                                                     next.execute();
                                                 }
                                            } catch (ExecutionException e) {
                                                //... stop chaining
                                                if (next!=null) {
                                                      next.cancel(false);
                                                }
                                            }
                                        }
                                  }
                                  Where you have now sw1,sw2 ,sw3 you'd create ChainingWorker sw1, ChainingWorker sw2 and chain them together:
                                     sw1.setNext(sw2.setNext(sw3)).execute();
                                  right at the code point where youcurrently have your ChainedTasks.execute() call.

                                  Edited by: isocdev_mb on Jan 27, 2010 2:58 AM
                                  • 14. Re: SwingWorker with JDK update 18
                                    843807
                                    Your approach seems correct for simple tasks, but mine involves a much complicated case; let me explain:
                                    I have extended SwingWorker with an AbstractSwingWorker that implements a bunch of interfaces in order to better monitor the task's execution e.g. Publisher-Subscriber pattern. Also my AbstractSwingWorker uses pluggable Decorators in order to output the results in Swings e.g. progressbar, textcomponents. My decorators also handle glasspane, disable window closing, changes mouse cursor and more. So I have created a handful of tasks for my app, simply by extending the AbstractSwingWorker and implementing only minimal methods like doInBackground() and done(). Later on I had to execute tasks that each one depended on the other, as I explained e.g. we must ensure that a person must have a unique SSN before actually trying to persist the SSN value. So I created the AbstractSwingWorkerChain that effectively extends the AbstractSwingWorker in order to achive that. Now my tasks can be executed individually or as part of a chain with no other code changes. The final code looks like that:

                                    1) Singe task execution; invoked from e.g. an Action
                                    // SwingWorker objects can't be reused, so create a new one as needed.
                                    DeletePersonTask task = new DeletePersonTask ( myPerson );
                                    task.attachDecorator( new ChangeMouseCursorDecorator(this) );
                                    task.attachDecorator( new TransparentWindowDecorator(this, true, true) );
                                    task.attachDecorator( new DisableWindowCloseDecorator(this) );
                                    task.addSubscriber(this); // custom update code!
                                    task.execute();
                                    2) Tasks in a chain; all must succeed and return an ok-to-go-on status; invoked from e.g. an Action
                                    // SwingWorker objects can't be reused, so create a new one as needed.
                                    /* 1st task */
                                    UniquePersonUsernameTask usernameTask = new UniquePersonUsernameTask(personId, personUsername);
                                    usernameTask.addSubscriber(this); // custom update code!
                                    /* 2nd task */
                                    UniquePersonCodeTask codeTask = new UniquePersonCodeTask(personId, personCode);
                                    codeTask.addSubscriber(this); // custom update code!
                                    /* 3rd task */
                                    UniquePersonIdcardTask idcardTask = new UniquePersonIdcardTask(personId, personIdCard);
                                    idcardTask.addSubscriber(this); // custom update code!
                                    /* 4th task */
                                    UniquePersonSsnTask ssnTask = new UniquePersonSsnTask(personId, personSSN);
                                    ssnTask.addSubscriber(this); // custom update code!
                                    /* 5th task */
                                    UniquePersonVatidTask vatidTask = new UniquePersonVatidTask(personId, personVatid);
                                    vatidTask.addSubscriber(this); // custom update code!
                                    /* 6th task */
                                    SavePersonTask saveTask = new SavePersonTask(personId);
                                    saveTask.addSubscriber(this); // custom update code!
                                    /* chain the tasks together */
                                    SavePersonChain chain = new SavePersonChain(
                                                usernameTask,
                                                codeTask,
                                                idcardTask,
                                                ssnTask,
                                                vatidTask,
                                                saveTask
                                    );
                                    chain.attachDecorator( new ChangeMouseCursorDecorator(this) );
                                    chain.attachDecorator( new TransparentWindowDecorator(this, true, true) );
                                    chain.attachDecorator( new DisableWindowCloseDecorator(this) );
                                    chain.attachDecorator( new ToggleActiveComponentsDecorator(saveButton) );
                                    chain.attachDecorator( new ProgressBarDecorator(progressBar) );
                                    _cancelButton.addActionListener(chain); // button's actionPerformed() will cancel the task!
                                    chain.addSubscriber(this); // custom update code!
                                    chain.execute();
                                    I tried to keep it as simple as possible...
                                    Anyway I really would like to opensource my implementation some day, from my Zeus-jscl project at [http://sourceforge.net/projects/zeus-jscl/]
                                    I am pretty sure that it is a must for real applications, because in my opinion Sun's SwingWorker is just too basic. If we should minimize code written over and over again, a better lib should be created.

                                    Regards
                                    Greg--

                                    PS: I could also implement the chain pattern, that you imply; I will investigate the amount of code changes required, because the event-chaining requirement appeared much later in our development process.
                                    1 2 Previous Next