8

I wanted to try out some ideas using SwingWorker since I haven't used it too much. Instead, I ran into an issue and I can't figure out what's wrong.

Here's a short SSCCE that demonstrates this problem (I know people here like SSCCEs):

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

public class SwingWorkerTest
{
    public static void main (String[] args)
    {
        SwingUtilities.invokeLater (new Runnable ()
        {
            @Override
            public void run ()
            {
                new MySwingWorker (500).execute ();
                new MySwingWorker (900).execute ();
                new MySwingWorker (1200).execute ();
            }
        });
    }
}

class MySwingWorker extends SwingWorker<Void, Void>
{
    private int ms;

    public MySwingWorker (int ms)
    {
        this.ms = ms;
    }

    @Override
    protected Void doInBackground()
    {
        Thread t = Thread.currentThread ();

        for (int i = 0; i < 50; i++)
        {
            try
            {
                Thread.sleep (ms);
            }
            catch (InterruptedException e)
            {
                e.printStackTrace ();
            }

            System.out.println ("I am thread with " + ms + " sleep in iteration " + i + ": " + t.getName () + " (" + t.getId () + ")");
        }

        return null;
    }
}

So my program should create 3 SwingWorkers that print a line on the screen then sleep for the specified number of milliseconds and then print the line again and sleep again etc. I create the SwingWorkers from within Swing's thread because otherwise they wouldn't even start.

So the expected output is:

I am thread with 500 sleep in iteration 0: SwingWorker-pool-1-thread-1 (15)
I am thread with 900 sleep in iteration 0: SwingWorker-pool-1-thread-2 (16)
I am thread with 500 sleep in iteration 1: SwingWorker-pool-1-thread-1 (15)
I am thread with 1200 sleep in iteration 0: SwingWorker-pool-1-thread-3 (17)
I am thread with 500 sleep in iteration 2: SwingWorker-pool-1-thread-1 (15)
I am thread with 900 sleep in iteration 1: SwingWorker-pool-1-thread-2 (16)
I am thread with 500 sleep in iteration 3: SwingWorker-pool-1-thread-1 (15)
I am thread with 1200 sleep in iteration 1: SwingWorker-pool-1-thread-3 (17)
I am thread with 500 sleep in iteration 4: SwingWorker-pool-1-thread-1 (15)
I am thread with 900 sleep in iteration 2: SwingWorker-pool-1-thread-2 (16)
I am thread with 500 sleep in iteration 5: SwingWorker-pool-1-thread-1 (15)
I am thread with 500 sleep in iteration 6: SwingWorker-pool-1-thread-1 (15)
I am thread with 900 sleep in iteration 3: SwingWorker-pool-1-thread-2 (16)
I am thread with 1200 sleep in iteration 2: SwingWorker-pool-1-thread-3 (17)
I am thread with 500 sleep in iteration 7: SwingWorker-pool-1-thread-1 (15)
.............

and so on for a total of 150 lines (3 workers x 50 iterations for each)

Instead, the output I get is:

I am thread with 500 sleep in iteration 0: SwingWorker-pool-1-thread-1 (15)
I am thread with 900 sleep in iteration 0: SwingWorker-pool-1-thread-2 (16)
I am thread with 500 sleep in iteration 1: SwingWorker-pool-1-thread-1 (15)

And that's it. Just 3 lines. After this the program exits. There's no stacktrace anywhere from that try catch block.

1). Where is the worker with the 1200ms sleep ? Actually, if I replace 1200ms with 1000ms, then this worker also prints 1 line... yes, only one. Weird...

2). Why do the workers stop ? (and I don't get 150 lines of stuff)

I ran this program using JRE 7 Update 11 on Windows 7 64-bit.

PS: I'm pretty sure that the bug mentioned here was fixed in this version of JRE, since I do get 2 distinct values (15 and 16) as thread ID's printed on the console. This was the first thing I suspected.

Community
  • 1
  • 1
Radu Murzea
  • 10,724
  • 10
  • 47
  • 69

4 Answers4

9

I believe that you need to show a visualized Swing top level Window in order to keep the Swing event thread alive. Otherwise the program will shut down for lack of non-daemon threads.

Edit:
To prove that the SwingWorker thread is a Daemon thread, just add a line of code to test it:

System.out.println("I am thread with " + ms + " sleep in iteration "
   + i + ": " + t.getName() + " (" + t.getId() + ")");

// **** added
System.out.println("Daemon?: " + Thread.currentThread().isDaemon()); 
Hovercraft Full Of Eels
  • 283,665
  • 25
  • 256
  • 373
9

If you look at line 771 of SwingWorker class source code (Java SE 7):

thread.setDaemon(true);

You will see that the SwingWorker is executed within a daemon thread, and in Java the JVM will be terminated if all non-daemon threads are finished.

Eng.Fouad
  • 115,165
  • 71
  • 313
  • 417
  • 4
    Holy crap, you guys are right. SwingWorkers run as daemon threads. This didn't even cross my mind. Thank you, StackOverflow, you always have the answer to everything :D . – Radu Murzea Feb 15 '13 at 18:23
4

As already mentioned, you can use the UI to keep the threads alive.

Alternatively, what you can do is use SwingWorker#get (or anything that prevents the main thread from terminating) to wait for the threads to finish. By doing so, you will get the output as expected. Here is the modified code, that does what you want.

import javax.swing.SwingUtilities;
import javax.swing.SwingWorker;
import java.util.concurrent.ExecutionException;

public class SwingWorkerTest
{
    public static void main (String[] args)
    {
        final MySwingWorker w1 = new MySwingWorker (500),
         w2 = new MySwingWorker (900),
         w3 = new MySwingWorker (1200);
        SwingUtilities.invokeLater (new Runnable ()
        {
            @Override
            public void run ()
            {
                w1.execute ();
                w2.execute ();
                w3.execute ();
            }
        });

        try{
            // you can replace the code in this block
            // by anything that keeps the program from 
            // terminating before the threads/SwingWorkers.
            w1.get();
            w2.get(); 
            w3.get(); 
        }catch(InterruptedException e){
            System.err.println("InterruptedException occured");
        }catch(ExecutionException e){
            System.err.println("InterruptedException occured");
        }

    }
}

class MySwingWorker extends SwingWorker<Void, Void>
{
    private int ms;

    public MySwingWorker (int ms)
    {
        this.ms = ms;
    }

    @Override
    protected Void doInBackground()
    {
        Thread t = Thread.currentThread ();

        for (int i = 0; i < 50; i++)
        {
            try
            {
                Thread.sleep (ms);
            }
            catch (InterruptedException e)
            {
                e.printStackTrace ();
            }

            System.out.println ("I am thread with " + ms + " sleep in iteration " + i + ": " + t.getName () + " (" + t.getId () + ")");
        }

        return null;
    }
}
Ankit
  • 6,772
  • 11
  • 48
  • 84
3

Have you tried creating any UI? or putting something like new Object().wait(); at the end of main, to prevent the main thread from exiting?

I'm not entirely sure that this is the case, but without any actual UI showing, i'm pretty sure the swing workers are just another thread, and you haven't configured any as daemon threads, so main starts them up, main finishes, and the process exits?

John Gardner
  • 24,225
  • 5
  • 58
  • 76