|
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
|
Enviado por Kams en agosto 13, 2005 a las 05:47 PM CEST #
Please define "better".
Thanks
Antonio
Enviado por Antonio en agosto 15, 2005 a las 05:28 PM CEST #
Enviado por Santhosh Kumar T en agosto 16, 2005 a las 12:56 AM CEST #
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 16, 2005 a las 06:15 AM CEST #
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 09:57 AM CEST #
Enviado por svenmeier en octubre 19, 2005 a las 03:58 PM CEST #