SwingWorker: stop that train!!

Or how to cancel SwingWorkers.

Imagine you have a SwingWorker that does some computations in the "doInBackground()" method. You want to cancel execution and you invoke "SwingWorker.cancel()". But, suprisingly, the job being performed is not cancelled. Your SwingWorker just keeps on doing the job!!

Why, you ask? Well, to understand why we need to go a little bit deep on how Threads are interrupt()ed (because SwingWorker.cancel() is just a hidden call to Thread.interrupt()). When you invoke "Thread.interrupt()" you're just setting a flag on the Thread, the "interrupt status" mentioned in the API, but you're not really interrupting the Thread, you're just setting a status flag.

Case I: If the operation you're performing on the "doInBackground()" method is not doing any I/O or is not wait()ing or sleep()ing (or doing those things mentioned in the API) then the Thread just keeps on running, executing its "run()" method (or "doInBackground()" in our case). No "InterruptedException" is thrown in this case.

Case II: On the other hand, if the operations in your SwingWorker.doInBackground() are performing I/O operations (such as reading data with JDBC or writing to disk) then the interrupted status is detected and an InterruptedException is thrown and, of course, the Thread just stops whatever it's doing.

So, if your "doInBackground()" fits in the "Case I" category then you need to take that into account when building your "doInBackground()" method. A good idea is to test if "Thread.currentThread().isInterrupted()" is true inside any loop you may have in your method. Another option is to invoke "Thread.currentThread().sleep()" (or Thread.sleep()) for some time in your loop. By doing so you allow "SwingWorker.cancel()" to really cancel whatever you're doing.

So here it comes my first suggestion for SwingWorkers:

SwingWorker suggestion I: make your doInBackground() cancellable
If your "doInBackground()" does not perform any I/O then either periodically sleep() from a little bit of time or check if Thread.currentThread().isInterrupted().

(Note, by the way, that this applies to any Thread you extend, not just SwingWorkers!).

If you see the source code of MandelbrotSwingWorker.java you'll notice that I'm sleeping for 1 millisecond each 5% of work done. Exact location is here:

120  if ( 0 == progress%5 ) 
121  {
122    setProgress( progress  );
123    // This is important!! I'll comment on this later.
124    Thread.sleep(1L);
125  }

By doing so I allow a MandelbrotSwingWorker (a SwingWorker that does not have any I/O operations) to be cancelled (and, well, slow things a little bit too, MandelbrotSwingWorker is too fast on my Pentium IV ;-).

I think this is good enough for today. Next week I'll talk about why cancelling SwingWorkers may not be a very good idea.

Happy Swinging,
Antonio

Comentarios:

good one. have u look into Foxtrot and Spin? they handle swing threading issues better thann swingworker. they uses swingworker internally though. i guess jdk 5 has some good stuff on this too. regards kams

Enviado por Kams en agosto 13, 2005 a las 10:47 AM CEST #

Hi Kams,

Please define "better".

Thanks
Antonio

Enviado por Antonio en agosto 15, 2005 a las 10:28 AM CEST #

You mentioned to call Thread.sleep(1L) so that InterruptedException is thrown. Does this method throw InterruptedException even if the thread has been interrupted before this call. I feel it throws. as Thread.interrupt() just makes a boolean flag to true. nice tip. Stopping SwingWorker is more important in some scenarios like: execution of a database query call.

Enviado por Santhosh Kumar T en agosto 15, 2005 a las 05:56 PM CEST #

The main advantage of Foxtrot is that the code can appear inline in your event handler. The call to Foxtrot's Worker doesn't return until after the offloaded task has completed, but EDT events will continue to be pumped anyway.

So it looks like this:
// Do something before doing stuff on another thread

Object result = Worker.post(new Task()
{
   public Object
   run()
   {
      // Do something on another thread
   }
});

// Do something afterwards (back on the EDT),
// possibly using the result from the other thread.
The other advantages are that the Task (unlike a Runnable) can return an Object and it's cousin Job can also throw Exceptions. This allows the final work (updating models or displaying error dialogs) to happen in the EDT without having to post yet another anonymous Runnable to the EventQueue.

Enviado por Graham Lea en agosto 15, 2005 a las 11:15 PM CEST #

Hi Graham,

I can do the same thing with SwingWorkers (I think). What about:

// Do something before doing stuff on another thread

SwingWorker<String,String>  task = 
  new SwingWorker<String,String>
  {
    public String doInBackground()
    {
      // Do something on another thread
      // (and return a String as result).
    }
  } );

task.execute();

// Do something afterwards (back on the EDT),
// possibly using the result from the
// swingworker "task" by invoking "task.get()".

So I don't really see the difference!.

The "doInBackground()" method can also throw Exceptions!!

So, I'm afraid, I can't still see the real advantage. BTW, can Foxtrot/Spin cancel() running tasks? I'm afraid not!!

So I think this is a matter of taste. I think SwingWorker is a Swiss army knife. I'll keep on playing with it. Keep tuned for more interesting entries to come.

Enviado por Antonio en agosto 16, 2005 a las 02:57 AM CEST #

A little bit late, but one objection to Antonios comment: Your code comments are misleading... // Do something afterwards (back on the EDT), // possibly using the result from the // swingworker "task" by invoking "task.get()" ... as this code will not be executed afterwards but in parallel to the SwingWorker. If you call task.get() you'll block the EDT, something you probably wanted to circumvent in the first place. Foxtrot and Spin are different! Sven

Enviado por svenmeier en octubre 19, 2005 a las 08:58 AM CEST #

Enviar un comentario:
Los comentarios han sido deshabilitados.
About

swinger

Search

Archives
« abril 2014
lunmarmiéjueviesábdom
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
    
       
Hoy