Hi folks. I have a pretty general question I was wondering if someone could answer. Should you always spawn potentially long running operations off on separate threads rather than executing them on the EDT? Or are there cases where it is OK to run potentially long running operations on the EDT?
My question has arisen mainly because I initially spawned such operations off to separate threads. Simply because I read that it was best practice to do so.
However, now that I have implemented my code in this manner, I am failing to see the benefit of it in my particular scenario.
My particular scenario is that I have an application where you can search for records and results are displayed. The flow is as follows:
1) User enters search criteria into a text field.
2) User clicks on a button.
3) I set the state of the cursor on the entire window to the state of WAIT_CURSOR.
4) I execute an operation that communicates with a server to carry out the search.
5) Search results are updated in a table. 6) I set the state of the cursor on the entire window to the state of DEFAULT_CURSOR.
Now I was carrying (4) and (5) out in a separate thread. But what am I actually gaining from this over just executing these two operations on the EDT?
The only reason I have read for spawning potentially long running operations off to separate threads, rather than executing on the EDT, is that if you execute them on the EDT it may cause your GUI to freeze up.
But in my particular example here, I don't want the user to be able to interact with the GUI whilst the operations (4) and (5) are being carried out. I set the WAIT_CURSOR on the entire window to prevent the user interacting with the application.
So would it be acceptable for me to execute all of my operations on the EDT? Or are there problems that I am missing here?
One thing I was thinking about was that a user can still resize my window even though I have set the cursor on the window to the state of WAIT_CURSOR.
So out of curiosity I decided to put my long running operation into a SwingWorker. I put operation on the server side to sleep for a few seconds just to simulate a very long operation. I then executed the operation through the GUI and attempt to resize the window. I was expecting the window to repaint\resize etc when I made it smaller, but it did not. The window didn't repaint and reorganise itself to fit the reduced screen size until the done() method of the SwingWorker had executed.
As you have mentioned, having a long-running operation does make the GUI unresponsive. And this is the main reason it is considered a best practice to move it separetely into a worker.
Even moving your mouse generates events on the EDT (say MouseListener), leave alone resizing - in this case events will queue up without getting executed making the GUI unresponsive.
As for your problem, you can make the buttons/menus disabled when the operation is happening and re-enable them in the done method.
However, I am curious about the test you tried and the results you got...so, I am going to try it out myself.
Sean Keane wrote:So out of curiosity I decided to put my long running operation into a SwingWorker. I put operation on the server side to sleep for a few seconds just to simulate a very long operation. I then executed the operation through the GUI and attempt to resize the window. I was expecting the window to repaint\resize etc when I made it smaller, but it did not. The window didn't repaint and reorganise itself to fit the reduced screen size until the done() method of the SwingWorker had executed.
In my app, the windows can be freely resized while SwingWorker executes. Didn't you call the lengthy operation from the done() method itself? SwingWorker.done() is in fact called on EDT.
Thanks for the replies guys. I haven't got my code infront of me here but I will check tonight. However I am pretty sure I did something incorrect in my code.
Originally I was spawning off the long running task in a normal thread. I thought when I ran this example that the contents of the window was not getting repainted\resized when I resized the window during the long running operation.
Last night I created an example that used the SwingWorker class instead. In this example, my window contents did get repainted\resized when I resized the window. So happy days
I've a feeling that maybe I was starting the thread incorrectly in my first example i.e. maybe using run() instead of start().
Below is a simple example that shows even a Thread on it's own (rather than the SwingWorker) should allow me to resize the window and for the window to be repainted\refreshed:
Martin Vajsar wrote:Yes, it's true. SwingWorker is an extension of such a simple background thread that provides means to communicate the progress or results of the processing to the EDT/user interface.
Very true. SwingWorker is a much needed class which should have been in the JDK long time back. With generics, it has become really cool to use.
Sean, also consider that you may want to be able to cancel a task, or update some sort of progress bar. You should always keep the UI responsive, even if there's nothing you can do with it at a given time.
Suppose I write a chat client - I would start a Thread to talk to the SocketServer and use the blocking 'read()' method in the thread. When I recieve message from the server, I would append it to the textArea. This call would be made in invokeLater.
Now, is SwingWorker a good candidate to replace the thread? i.e technically yes, but it doesn't sound the right fit to me. This is because, I would append to the textAre via the 'process' method - but in this case, the done will be called only when socket closes. SwingWorker is good for long-running task...but what about continous-running job? - or is this just gobbledegook?
That's what publish and process are for. You publish from the doInBackground method, and then at some time process is called on the EDT with everything that's published so far. It's important to know that this list may contain elements from more than 1 publish call. So in this case, the SwingWorker could like like this (pseudo code):
Darryl Burke wrote:why on earth are you resisting the perfectly good approach that Rob Spoor has given you?
I'm not. I'm using it in conjunction with it
If you need to perform radically different actions on the GUI thread, you might simply pass instances of Runnables to the publish() method and then call run() in the process() method. That way you'll eliminate the need for invokeLater():
I haven't ever done this, but I'd say it should work. It might not be the cleanest design, though probably still better than mixing publish()/process() with invokeLater()
I have to admit I once used invokeAndWait(), because I needed user interaction right in the middle of the processing. I don't know whether there are better alternatives to this.
My particular case is that I am calling out to my business layer (using RMI) in the doInBackground method. Any network call is a potentially long running operation, that's the reason I'm using the SwingWorker.
The RMI method call could throw an error. If this happens I want to inform the user (via the GUI) that something has gone wrong. So I launch a JDialog when I catch the Exception. The launching of this JDialog is wrapped in an invokeLater.
It works . I just was not sure if it was the correct thing to do, whether there are better patterns to follow, or even if it could cause me problems.