Home > Archive > Java Help > January 2006 > Swing Components Freeze During Other Actions Even With Threads
You are viewing an archived Text-only version of the thread.
To view this thread in it's original format and/or if you want to reply to
this thread please [click here]
| Author |
Swing Components Freeze During Other Actions Even With Threads
|
|
| Hal Vaughan 2006-01-18, 10:01 pm |
| I'm having a variation on an old problem: After "Okay" is clicked in a
window, I need to run an external program and read the output. I have my
code to do this in a thread, which should help, right? No. It doesn't.
Here's the problem: I'm waiting for the output of the external process
before I can open a message window and say everything is okay or it won't
work. That means I have this:
emitNotification("Verifying");
verifyProgram.start();
while (!verifyProgram.isDone()) {
try {
Thread.sleep(10);
} catch (Exception e) {
System.err.println("Error: Interrupted while sleeping!");
}
}
if (verifyProgram.isValidated()) {
emitError("Invalid choice");
return false;
}
emitNotification("Verification worked");
Since I have to wait in the main thread until verifyProgram is done, I have
the same problem as if I were not using a thread: While the main thread is
sleeping, it does not update the Swing GUI.
So how do you wait on a thread and still allow the current thread to update
the GUI?
Thanks!
Hal
| |
| Thomas Hawtin 2006-01-18, 10:01 pm |
| Hal Vaughan wrote:
>
> Since I have to wait in the main thread until verifyProgram is done, I have
> the same problem as if I were not using a thread: While the main thread is
> sleeping, it does not update the Swing GUI.
>
> So how do you wait on a thread and still allow the current thread to update
> the GUI?
It will be the AWT Event Dispatch Thread, not the main thread (the main
thread being the thread in which your public static void main(String[]
args) method is invoked to start your application).
Don't wait for the thread. Certainly don't do it by polling. Instead get
the thread to call you back using EventQueue.invokeLater.
http://download.java.net/jdk6/docs/...tml#invokeLater(java.lang.Runnable)
http://java.sun.com/docs/books/tuto...sc/threads.html
Tom Hawtin
--
Unemployed English Java programmer
http://jroller.com/page/tackline/
| |
| Roedy Green 2006-01-18, 10:01 pm |
| On Wed, 18 Jan 2006 21:05:56 -0500, Hal Vaughan
<hal@thresholddigital.com> wrote, quoted or indirectly quoted someone
who said :
>So how do you wait on a thread and still allow the current thread to update
>the GUI?
you do the waiting for the exec to finish on a sub thread, not the
main thread.
--
Canadian Mind Products, Roedy Green.
http://mindprod.com Java custom programming, consulting and coaching.
| |
| Hal Vaughan 2006-01-19, 4:09 am |
| Roedy Green wrote:
> On Wed, 18 Jan 2006 21:05:56 -0500, Hal Vaughan
> <hal@thresholddigital.com> wrote, quoted or indirectly quoted someone
> who said :
>
>
>
> you do the waiting for the exec to finish on a sub thread, not the
> main thread.
I can see that, but I need the results of the sub thread. How do I let
Swing update (and possibly take other events) and still know when to get
the results without some kind of loop-sleep-check results-endloop
structure?
Hal
| |
| Tony Morris 2006-01-19, 4:09 am |
| "Hal Vaughan" <hal@thresholddigital.com> wrote in message
news:SfKdnf6tDIsvh1LeRVn-rA@comcast.com...
> Roedy Green wrote:
>
>
> I can see that, but I need the results of the sub thread. How do I let
> Swing update (and possibly take other events) and still know when to get
> the results without some kind of loop-sleep-check results-endloop
> structure?
>
> Hal
"Register" a callback.
Pass something that is called back on when the results are available.
--
Tony Morris
http://tmorris.net/
Java Questions and Answers
http://jqa.tmorris.net/
| |
| Hal Vaughan 2006-01-19, 4:09 am |
| Tony Morris wrote:
> "Hal Vaughan" <hal@thresholddigital.com> wrote in message
> news:SfKdnf6tDIsvh1LeRVn-rA@comcast.com...
>
> "Register" a callback.
> Pass something that is called back on when the results are available.
You mean pass a method to it? Then don't I still have to wait on that
value? If not, what do I do? Just sleep and the callback will wake the
thread?
Hal
| |
| Roedy Green 2006-01-19, 4:09 am |
| On Wed, 18 Jan 2006 23:51:02 -0500, Hal Vaughan
<hal@thresholddigital.com> wrote, quoted or indirectly quoted someone
who said :
>I can see that, but I need the results of the sub thread. How do I let
>Swing update (and possibly take other events) and still know when to get
>the results without some kind of loop-sleep-check results-endloop
>structure?
The subthread gets the results and schedules whatever has to be done
with them. You might use Listeners for notification.
The main thread finds out simply by the presence of new data it
stumbles on in the course of its ordinary activities.
--
Canadian Mind Products, Roedy Green.
http://mindprod.com Java custom programming, consulting and coaching.
| |
| Roedy Green 2006-01-19, 4:09 am |
| On Thu, 19 Jan 2006 02:13:21 -0500, Hal Vaughan
<hal@thresholddigital.com> wrote, quoted or indirectly quoted someone
who said :
>You mean pass a method to it? Then don't I still have to wait on that
>value? If not, what do I do? Just sleep and the callback will wake the
>thread?
Why bother with two threads then? Just have the subthread do whatever
it is needs be done when the information has arrived.
--
Canadian Mind Products, Roedy Green.
http://mindprod.com Java custom programming, consulting and coaching.
| |
| Hal Vaughan 2006-01-19, 4:09 am |
| Roedy Green wrote:
> On Thu, 19 Jan 2006 02:13:21 -0500, Hal Vaughan
> <hal@thresholddigital.com> wrote, quoted or indirectly quoted someone
> who said :
>
>
> Why bother with two threads then? Just have the subthread do whatever
> it is needs be done when the information has arrived.
The subthread is supplying the information.
I'm sure there are ways to do this, and maybe someone has pointed it out. I
just don't get it -- seriously. I'm trying to figure out how to do this.
This is on one in a series of panels for setting up a program. The user has
selected a path where the program should be, then clicks "Next". A message
dialogue opens, with a "We're verifying the location" message. Then I run
the program (if it is where it is supposed to be) with a "--version"
argument. I read the output and scan it for the version number. If the
version is acceptable, a new message window opens, saying it will work. If
it is an unacceptable version, a window opens saying it won't work.
On the first dialogue ("We're verifying..."), once I click "Okay", the
message dialogue window goes blank and the entire window goes blank with
it. Nothing happens while the program is being run, printing out the
version, and I'm scanning for the version number. Once that is done, Swing
catches up.
So basically, I need to let the user click "Okay", then, ideally, Swing
would update the window, THEN check the version. But that doesn't happen,
which I know is normal. So I dispatch a thread to check the version
number, but I can't do a thing on the main thread until I know if the
version is acceptable.
So, no matter what I do, I have to wait for the external program to run
before saying it works or won't work. The hard part is I can't get Swing
to update while waiting for the 2nd thread to run the program. I'm still
reading up on what some other responses have said. I know one time I
experimented with invokeLater() and invokeAndWait(), but I've never gotten
them to work properly either.
I have yet to see how I can get Swing to update, then wait for information
in its own thread or a subthread.
Since I am sure this is not the kind of thing the Java team would make this
difficult, I'm sure I'm missing something basic here.
Hal
| |
| Thomas Weidenfeller 2006-01-19, 4:09 am |
| Hal Vaughan wrote:
> Since I am sure this is not the kind of thing the Java team would make this
> difficult, I'm sure I'm missing something basic here.
It's not difficult. It has been a done a thousand times. Time consuming
tasks have to be done on a separate thread. Period. Separate threads
which want to update the GUI need to use invokeLater() (invokeAndWait()
often doesn't make sense). That's it.
Five to ten lines of code. Explained in each and every AWT and Swing
tutorial, book, FAQ, reference, overview, etc.
/Thomas
--
The comp.lang.java.gui FAQ:
ftp://ftp.cs.uu.nl/pub/NEWS.ANSWERS...ng/java/gui/faq
http://www.uni-giessen.de/faq/archi...g.java.gui.faq/
| |
| Roedy Green 2006-01-19, 4:09 am |
| On Thu, 19 Jan 2006 03:08:23 -0500, Hal Vaughan
<hal@thresholddigital.com> wrote, quoted or indirectly quoted someone
who said :
>The subthread is supplying the information.
I am don't follow what you are saying but I think the problem may come
from thinking that a thread belongs to a set of classes. Threads can
wander all over the place executing code from anywhere. That is not
particularly naughty. So the thread that notices data has arrived can
be the one to process it, using methods, objects and classes you might
normally think of as "belonging" to the main thread.
--
Canadian Mind Products, Roedy Green.
http://mindprod.com Java custom programming, consulting and coaching.
| |
| Hal Vaughan 2006-01-19, 7:08 pm |
| Thomas Weidenfeller wrote:
> Hal Vaughan wrote:
>
> It's not difficult. It has been a done a thousand times. Time consuming
> tasks have to be done on a separate thread. Period. Separate threads
> which want to update the GUI need to use invokeLater() (invokeAndWait()
> often doesn't make sense). That's it.
>
> Five to ten lines of code. Explained in each and every AWT and Swing
> tutorial, book, FAQ, reference, overview, etc.
That's oversimplifying it.
I've checked a number or sources. I've found stuff on how to invoke a
thread, use invokeLater() and invokeWait(), but that doesn't answer the
whole question.
I have a series of events (if everything checks out):
A) "Next" is clicked to move to another panel
B) A "Verifying" message appears, asking the user to click "Okay"
C) An external program is run to get the version info (and checks it)
D) A few flags are set and a "Version okay" message window opens
E) The user clicks "Okay" on the message window and the method returns to
the caller, which displays the next panel.
I have no problem with A & B. To get C to work, I can use a thread, or
invokeLater(), but here is where the problem occurs, and this is what I
either don't understand, or have not fond an answer to: the main thread
NEEDS the results generated in the 2nd thread.
So what do I do in the main thread, that allows the display to be updated,
but still waits until the 2nd thread is done? (Or, when the 2nd thread is
done, can get the data from it?) I've tried a loop to sleep and check if
the 2nd thread is done, but even Thread.sleep, but even sleep keeps the
panel from updating itself.
So how can the main thread wait on the 2nd thread AND update the display
while waiting?
Hal
| |
| Steve Horsley 2006-01-20, 7:11 pm |
|
> That's oversimplifying it.
>
> I've checked a number or sources. I've found stuff on how to invoke a
> thread, use invokeLater() and invokeWait(), but that doesn't answer the
> whole question.
>
> I have a series of events (if everything checks out):
>
> A) "Next" is clicked to move to another panel
> B) A "Verifying" message appears, asking the user to click "Okay"
> C) An external program is run to get the version info (and checks it)
> D) A few flags are set and a "Version okay" message window opens
> E) The user clicks "Okay" on the message window and the method returns to
> the caller, which displays the next panel.
>
> I have no problem with A & B. To get C to work, I can use a thread, or
Since C takes significant time, you MUST use a thread, or the GUI
goes to sleep.
> invokeLater(), but here is where the problem occurs, and this is what I
No no NO. invokeLater asks the GUI event thread to do it. If you
do that, the GUI will go to sleep while the event thread is busy
doing C.
> either don't understand, or have not fond an answer to: the main thread
> NEEDS the results generated in the 2nd thread.
>
Actually, the main thread (I presume you mean the GUI thread)
does NOT need the data. The GUI class needs the data. This is
important. Understand the difference.
The GUI class needs the data, and a Version-OK dialog needs
displaying. The easiest thing would be to get the sub-thread that
just did C to set the flags in the GUI class and then create the
dialog, but this is not thread safe - it is just possible that
the GUI event thread might be asked to do something to the GUI
state while the sub-thread is busy changing things - crash.
So the sub-thread bundles all the required data into a Runnable
who's run() method will update the GUI class and create the
Version-OK dialog, and use invokeLater() to pass the Runnable to
the event thread. The sub-thread can now die happy. The event
thread will in the fullness of time find the Runnable waiting to
be run. In this way, only the event thread actually ever updates
the GUI class state.
Note that in this case we DID need the event thread to have the
data, but only because we wanted to avoid concurrent modification
issues. If we implemented some thread-safe locking, we could have
don the update to the GUI state from the sub-thread. I am being
pedantic about this because it is important to know where data
lives. You mentioned setting a couple of flags. The flags are in
the GUI class, NOT in any particular thread. Any thread could in
principle set or read those flags by visiting the Gui class. I
really think it helps to see your application as a bunch of
static objects with values written on them and a bunch of worker
threads running round visiting those objects reading and setting
values. Like engineers visiting benches in a factory.
> So what do I do in the main thread, that allows the display to be updated,
> but still waits until the 2nd thread is done? (Or, when the 2nd thread is
> done, can get the data from it?) I've tried a loop to sleep and check if
> the 2nd thread is done, but even Thread.sleep, but even sleep keeps the
> panel from updating itself.
Before launching the thread, set a busy flag. Have the callback
clear the busy flag after setting the results.
When the user clicks the Go button, check the busy flag, and do
nothing if you're already busy.
>
> So how can the main thread wait on the 2nd thread AND update the display
> while waiting?
>
The GUI thread doesn't wait for the sub-thread to finish. It
waits for an event on the event queue. That could be a repaint
request, or could be the arrival of and invokeLater() object that
carries the answer the user is waiting for. The main thing here
is that you have turned the arrival of the answer into an event.
Your GUI is event driven, it does not do polling.
HTH
Steve
| |
| Hal Vaughan 2006-01-20, 7:11 pm |
| Steve Horsley wrote:
>
>
> Since C takes significant time, you MUST use a thread, or the GUI
> goes to sleep.
Okay, I know that, so I follow so far.
>
> No no NO. invokeLater asks the GUI event thread to do it. If you
> do that, the GUI will go to sleep while the event thread is busy
> doing C.
I didn't realize that when I posted. I found out this morning what you just
pointed out. That's frustrating, because I've tried to get help on this
before, and the info was NOT complete, and it was not spelled out in my
books I use. Now I see that. It's a major point, so thanks!
>
> Actually, the main thread (I presume you mean the GUI thread)
> does NOT need the data. The GUI class needs the data. This is
> important. Understand the difference.
I see what you mean. Thanks for putting it clearly. You're right, and I
had not seen that before.
> The GUI class needs the data, and a Version-OK dialog needs
> displaying. The easiest thing would be to get the sub-thread that
> just did C to set the flags in the GUI class and then create the
> dialog, but this is not thread safe - it is just possible that
> the GUI event thread might be asked to do something to the GUI
> state while the sub-thread is busy changing things - crash.
>
> So the sub-thread bundles all the required data into a Runnable
> who's run() method will update the GUI class and create the
> Version-OK dialog, and use invokeLater() to pass the Runnable to
> the event thread. The sub-thread can now die happy. The event
> thread will in the fullness of time find the Runnable waiting to
> be run. In this way, only the event thread actually ever updates
> the GUI class state.
Let me restate it, in my words, to make sure I understand it:
I can go on and update the GUI with settings and commands in the main thread
and take the runnable object, which will need all the data for all the
non-GUI stuff it does, and run it (Object.start()). Then, to keep it
thread safe, when the sub-thread is done, and needs to put up a message
saying, "Everything's working!", I do that with an invokeLater() -- within
the sub-thread and what it invokesLater is putting up the message window.
By now, unless the user has pressed other buttons, the main thread is idle.
> Note that in this case we DID need the event thread to have the
> data, but only because we wanted to avoid concurrent modification
> issues. If we implemented some thread-safe locking, we could have
> don the update to the GUI state from the sub-thread. I am being
> pedantic about this because it is important to know where data
> lives. You mentioned setting a couple of flags. The flags are in
> the GUI class, NOT in any particular thread. Any thread could in
> principle set or read those flags by visiting the Gui class. I
> really think it helps to see your application as a bunch of
> static objects with values written on them and a bunch of worker
> threads running round visiting those objects reading and setting
> values. Like engineers visiting benches in a factory.
This helps a lot! It is much clearer than it was. I have a class that does
a recursive directory search and I'm combining that with the file/version
verifier and setting it up so it can be all together, in one Runnable
object. I'll just set all the variables ahead of time and run it as a
thread when it's time. It will use invokeLater() to pop up the message
window when it's done.
>
> Before launching the thread, set a busy flag. Have the callback
> clear the busy flag after setting the results.
>
> When the user clicks the Go button, check the busy flag, and do
> nothing if you're already busy.
That's a good idea. Thanks, again.
>
> The GUI thread doesn't wait for the sub-thread to finish. It
> waits for an event on the event queue. That could be a repaint
> request, or could be the arrival of and invokeLater() object that
> carries the answer the user is waiting for. The main thing here
> is that you have turned the arrival of the answer into an event.
That's a major change in how I was thinking of how to do it. I'm self
taught, and had been programming for a few years before I started using a
GUI as a front end. Most of the coding I'm doing is in Perl, without any
GUI, so I don't always catch all the nuances of how the flow is dispatched
events as opposed to a linear progression of events in the order I pick.
> Your GUI is event driven, it does not do polling.
>
>
> HTH
> Steve
I don't want to gush, but I'll add that I used to be a teacher (especially
working with students with Learning Disabilities), and had done a lot of
work in focusing on how people learn and how I can present information to
them. I've been stuck on this issue, on and off, for a couple years. I've
always found a work around in how I set up the GUI. I have had various
people help me, but I do want to say that, as a former teacher, I wanted to
compliment you on your ability to put this clearly and with examples that
make it easy to understand what is going on.
I really do appreciate your patience and completeness in your answer.
I'll post the final result of what I'm doing.
Hal
| |
| Hal Vaughan 2006-01-20, 9:57 pm |
| Steve Horsley wrote:
....
>
> The GUI thread doesn't wait for the sub-thread to finish. It
> waits for an event on the event queue. That could be a repaint
> request, or could be the arrival of and invokeLater() object that
> carries the answer the user is waiting for. The main thing here
> is that you have turned the arrival of the answer into an event.
I found a spot here I didn't understand (all the rest was a HUGE help, as
I've already posted in a reply).
I am not sure what I invokeLater() or how I can call back the original class
to alter data in it. I've just posted a new thread on this, since it is a
different problem, but any help in how to call back the calling class (to
update data) or any way to add a listener in the first class, attach it to
this class, and have this class force or trigger an event to call that
listener would be a big help!
Thanks!
Hal
| |
| Steve Horsley 2006-01-27, 7:03 pm |
| Hal Vaughan wrote:
> Steve Horsley wrote:
> ...
>
> I found a spot here I didn't understand (all the rest was a HUGE help, as
> I've already posted in a reply).
>
The GUI thread does not wait for the sub-thread to finish, as
such. It launches the sub-thread and immediately returns to its
little loop waiting for an event , any event, to appear on the
event queue.
It's like telling a servant to go and do a job, and let me know
when it's done. e.g. "Go to Antarctica and count all the
penguins. Send me the final count in a letter." You don't just
hang around the letterbox waiting for the letter and ignoring
everything else - you get back to your life, which happens to
include opening letters.
The event that carries the answer is just one of many events that
the GUI is waiting for. It is not waiting for THE ANSWER to the
exclusion of all other events. It will still process repaint
requests, button clicks etc. When the letter with the penguin
count arrives, it will be handled appropriately by the event
dispatcher. In this case, it will be a Runnable that needs its
run() method calling. I guess the run() method will contain
something like:
public class PenguinCountAnswer implements Runnable {
private GUI myGUI;
private long totalResult;
public PenguinCountAnswer(GUI g) {
myGui = g;
}
public setResult(long r) {
totalResult = r;
}
public void run() {
myGui.setPenguinCount(totalResult);
myGui.setBusyFlag(false);
myGui.showGotTheAnswerDialog();
}
And you see, since the code is in the Runnable and is being
invoked later by the event dispatch thread, it is the event
thread that is making the GUI changes. Since it is only ever the
event thread that updates the GUI, there is no danger of
concurrent modification.
> I am not sure what I invokeLater() or how I can call back the original class
> to alter data in it. I've just posted a new thread on this, since it is a
> different problem, but any help in how to call back the calling class (to
> update data) or any way to add a listener in the first class, attach it to
> this class, and have this class force or trigger an event to call that
> listener would be a big help!
>
> Thanks!
>
> Hal
The sequence might be...
User clicks the Count Penguins button.
// Create a PenguinCountAnswer to carry the answer back
// this is the class described above
PenguinCountAnswer pca = new PenguinCountAnswer(this);
// Create a penguinCounter and give it the Answer
container // to give back to us
PenguinCounterTask task = new PenguinCounterTask(pca);
new Thread(task).start()
The task scuttles off for a while, and then finally posts the
result...
answer = countPenguins(); // this takes a while
pca.setResult(answer);
SwingUtilities.invikeLater(pca);
The complication is that the Runnable needs to have a reference
to the GUI class to be able to update it. So the GUI has to
create the Runnable, giving the constructor a this reference. It
then passes the Runnable to the subthread, which can call its
setAnswer() method, and then pass it back to the GUI using
SwingUtilities.invokeLater.
Steve
| |
| Hal Vaughan 2006-01-28, 7:05 pm |
| Steve Horsley wrote:
> Hal Vaughan wrote:
Okay!
Got it now. Thanks for all the help. Before this I had an entirely
inaccurate view of how to use Swing for events and functions. With a
proper understanding, it is not only clearer, but much easier to deal with!
Thanks again!
Hal
[color=darkred]
> The GUI thread does not wait for the sub-thread to finish, as
> such. It launches the sub-thread and immediately returns to its
> little loop waiting for an event , any event, to appear on the
> event queue.
>
> It's like telling a servant to go and do a job, and let me know
> when it's done. e.g. "Go to Antarctica and count all the
> penguins. Send me the final count in a letter." You don't just
> hang around the letterbox waiting for the letter and ignoring
> everything else - you get back to your life, which happens to
> include opening letters.
>
> The event that carries the answer is just one of many events that
> the GUI is waiting for. It is not waiting for THE ANSWER to the
> exclusion of all other events. It will still process repaint
> requests, button clicks etc. When the letter with the penguin
> count arrives, it will be handled appropriately by the event
> dispatcher. In this case, it will be a Runnable that needs its
> run() method calling. I guess the run() method will contain
> something like:
>
> public class PenguinCountAnswer implements Runnable {
> private GUI myGUI;
> private long totalResult;
> public PenguinCountAnswer(GUI g) {
> myGui = g;
> }
> public setResult(long r) {
> totalResult = r;
> }
> public void run() {
> myGui.setPenguinCount(totalResult);
> myGui.setBusyFlag(false);
> myGui.showGotTheAnswerDialog();
> }
>
> And you see, since the code is in the Runnable and is being
> invoked later by the event dispatch thread, it is the event
> thread that is making the GUI changes. Since it is only ever the
> event thread that updates the GUI, there is no danger of
> concurrent modification.
>
>
> The sequence might be...
>
> User clicks the Count Penguins button.
>
> // Create a PenguinCountAnswer to carry the answer back
> // this is the class described above
> PenguinCountAnswer pca = new PenguinCountAnswer(this);
>
> // Create a penguinCounter and give it the Answer
> container // to give back to us
> PenguinCounterTask task = new PenguinCounterTask(pca);
> new Thread(task).start()
>
> The task scuttles off for a while, and then finally posts the
> result...
>
> answer = countPenguins(); // this takes a while
> pca.setResult(answer);
> SwingUtilities.invikeLater(pca);
>
>
> The complication is that the Runnable needs to have a reference
> to the GUI class to be able to update it. So the GUI has to
> create the Runnable, giving the constructor a this reference. It
> then passes the Runnable to the subthread, which can call its
> setAnswer() method, and then pass it back to the GUI using
> SwingUtilities.invokeLater.
>
> Steve
|
|
|
|
|