Invoke Later Question

J

John Raggio

I have done some reading on this and think that I understand it. I
have a some questions regarding a test program that I wrote. It
essentially tests menu items that lauch lengthy operations. I will
paste it in below.

1) If I have an action that takes some time, is there any way to have
the menu item that was clicked be dismissed before starting the
lengthy action? Depending on the L&F in use either the menu and its
text remains after clicking or the text is removed leaving only a gray
box behind. I realize the correct thing to do is to spawn a thread to
do the work, but I'd like to cheat a bit if I could.

2) Please explain why the method doItThreadedLater() takes MUCH longer
to run than doItThreaded(). My guess is that the overhead involved
with creating the extra objects and threads takes resources and time.
I also suspect that the fact that 4M events needs to be put on the
event queuue and processed may come into play. I also suspect that
doItThreaded() will allow for paints() to be combined more often. In
this case it just seems that doing it the right way takes much loinger
to execute, but is still the way to go.

I look forward to any commments.

Thanks,
John

+++++++++++++++++++++++++++++++++



import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.HeadlessException;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;

import javax.swing.JDialog;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JProgressBar;
import javax.swing.SwingConstants;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;

/**
* @author raggioj, Feb 15, 2004
*/
public class TestMenu extends JFrame {
private static final int MAX = 4000000;
JProgressBar pb;

/**
* @throws java.awt.HeadlessException
*/
public TestMenu() throws HeadlessException {
super();
}

public TestMenu(String title){
super(title);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setSize(400,200);
String cplf = UIManager.getCrossPlatformLookAndFeelClassName();
String slf = UIManager.getSystemLookAndFeelClassName();

try {

// threaded looks fine and non threaded menu and menu text remains
when
// selecting non-threaded
// UIManager.setLookAndFeel(cplf);

// threaded works fine and non-threaded menu text dismisses, but
gray box remains
// sometimes leads to threaded version working inconsistently and
even out
// of memeory errors
UIManager.setLookAndFeel(slf);

// threaded looks fine and non threaded menu and menu text remains
when
// selecting non-threaded
// UIManager.setLookAndFeel("com.sun.java.swing.plaf.windows.WindowsLookAndFeel");

// threaded looks fine and non threaded menu and menu text remains
when
// selecting non-threaded
// UIManager.setLookAndFeel("com.sun.java.swing.plaf.motif.MetalLookAndFeel");

// threaded looks fine and non threaded menu and menu text remains
when
// selecting non-threaded
// UIManager.setLookAndFeel("com.sun.java.swing.plaf.motif.MotifLookAndFeel");

// threaded looks fine and non threaded menu and menu text remains
when
// selecting non-threaded
// UIManager.setLookAndFeel("javax.swing.plaf.basic.BasicLookAndFeel");

}
catch (Exception e1) {}

JMenu menu = new JMenu("Window");
menu.setMnemonic(KeyEvent.VK_W);
JMenuItem item = null;

item = new JMenuItem("Threaded");
item.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
MyDialog d = new MyDialog("Threaded");
d.show();
d.doItThreaded();
}
});
menu.add(item);

item = new JMenuItem("Threaded Using Invoke Later");
item.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
MyDialog d = new MyDialog("Threaded Using Invoke
Later");
d.show();
d.doItThreadedLater();
}
});
menu.add(item);

menu.addSeparator();

item = new JMenuItem("Non-Threaded");
item.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
MyDialog d = new MyDialog("Non-Threaded");
d.show();
d.doItNonThreaded();
}
});
menu.add(item);

JMenuBar menuBar = new JMenuBar();
menuBar.add(menu);
setJMenuBar(menuBar);

JLabel l = new
JLabel(UIManager.getLookAndFeel().getClass().getName(),
SwingConstants.CENTER);
getContentPane().setBackground(Color.CYAN);
getContentPane().add(l,BorderLayout.CENTER);

}

public static void main(String[] args) {
final TestMenu t = new TestMenu("Menu Test App");
t.show();
}


class MyDialog extends JDialog{
JProgressBar pb;

public MyDialog(String title){
super(TestMenu.this, "Test Dialog : " + title);
setSize(400,200);
setLocation(300,300);

getContentPane().add(new JLabel("Progress:"), BorderLayout.NORTH);

pb = new JProgressBar();
pb.setPreferredSize(new Dimension(400, 50));
pb.setMaximum(MAX);
getContentPane().add(pb, BorderLayout.CENTER);

}

public void doItThreaded(){
new Thread(){
public void run(){
for(int i =0; i<= MAX ; i++){
pb.setValue(i);
}
}
}.start();
}

// MUCH slower using invoke later to update the gui than not
public void doItThreadedLater(){
new Thread(){
public void run(){
for(int i =0; i<= MAX ; i++){
final int t = i;
SwingUtilities.invokeLater( new Runnable() {
public void run() {
pb.setValue(t);
}
}
);
}
}
}.start();

}

public void doItNonThreaded(){
for(int i =0; i<= MAX ; i++){
pb.setValue(i);
}


/*
SwingUtilities.invokeLater(new Runnable() {
public void run() {
for(int i =0; i<= MAX ; i++){
pb.setValue(i);
}
}
});
*/

}
}


}
 
C

Chris Smith

John said:
1) If I have an action that takes some time, is there any way to have
the menu item that was clicked be dismissed before starting the
lengthy action? Depending on the L&F in use either the menu and its
text remains after clicking or the text is removed leaving only a gray
box behind. I realize the correct thing to do is to spawn a thread to
do the work, but I'd like to cheat a bit if I could.

Placing the first call to your code inside an invokeLater call might
accomplish this.
2) Please explain why the method doItThreadedLater() takes MUCH longer
to run than doItThreaded(). My guess is that the overhead involved
with creating the extra objects and threads takes resources and time.
I also suspect that the fact that 4M events needs to be put on the
event queuue and processed may come into play.

All of the above and more. You'd be better off picking a lower level of
granularity for updates to the progress bar. If your value changes 4
million time, you might try updating only once every 10,000 iterations
or so. A lot of the performance difficulty most likely comes from the
constant flip-flop between one thread and the other trying to use the
same data structures (namely, the event queue).
I also suspect that
doItThreaded() will allow for paints() to be combined more often.

Not exactly, but it does prevent a new event from being logged on the
event queue every time you increment by one. The paint events are
likely collapsed in both cases, but it's those internal events generated
by invokeLater that cause the problem.
In this case it just seems that doing it the right way takes much loinger
to execute, but is still the way to go.

Or reduce your updates as explained above. When you are constantly
reaching a point where you could update the GUI, it's often a good ide
to see if your update is really changing things by much before doing so.
So a general pattern would be.

private int lastUpdate;

public void update(JProgressBar b, int val)
{
final int MIN_UPDATE_DIFF = 10000;

if (val - lastUpdate >= MIN_UPDATE_DIFF)
{
SwingUtilities.invokeLater(...);
lastUpdate = val;
}
}

--
www.designacourse.com
The Easiest Way to Train Anyone... Anywhere.

Chris Smith - Lead Software Developer/Technical Trainer
MindIQ Corporation
 
B

Bryan E. Boone

invokeLater puts the Runnable at the end of the event thread, so
your menu should close before the run in the Runnable is executed.
When it is executed, it is executed on the event thread, so if it takes a
long time
to complete, your UI will still appear to hang.
Running it on a different thread, the run is executed, of course on the
thread (not the
even thread) so your UI will not appear to hang. If your thread needs to
update the UI
when finished, you need to hook back into the event thread by either using
invokeLater
or invokeAndWait.

-Bryan


John Raggio said:
I have done some reading on this and think that I understand it. I
have a some questions regarding a test program that I wrote. It
essentially tests menu items that lauch lengthy operations. I will
paste it in below.

1) If I have an action that takes some time, is there any way to have
the menu item that was clicked be dismissed before starting the
lengthy action? Depending on the L&F in use either the menu and its
text remains after clicking or the text is removed leaving only a gray
box behind. I realize the correct thing to do is to spawn a thread to
do the work, but I'd like to cheat a bit if I could.

2) Please explain why the method doItThreadedLater() takes MUCH longer
to run than doItThreaded(). My guess is that the overhead involved
with creating the extra objects and threads takes resources and time.
I also suspect that the fact that 4M events needs to be put on the
event queuue and processed may come into play. I also suspect that
doItThreaded() will allow for paints() to be combined more often. In
this case it just seems that doing it the right way takes much loinger
to execute, but is still the way to go.

I look forward to any commments.

Thanks,
John

+++++++++++++++++++++++++++++++++



import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.HeadlessException;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;

import javax.swing.JDialog;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JProgressBar;
import javax.swing.SwingConstants;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;

/**
* @author raggioj, Feb 15, 2004
*/
public class TestMenu extends JFrame {
private static final int MAX = 4000000;
JProgressBar pb;

/**
* @throws java.awt.HeadlessException
*/
public TestMenu() throws HeadlessException {
super();
}

public TestMenu(String title){
super(title);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setSize(400,200);
String cplf = UIManager.getCrossPlatformLookAndFeelClassName();
String slf = UIManager.getSystemLookAndFeelClassName();

try {

// threaded looks fine and non threaded menu and menu text remains
when
// selecting non-threaded
// UIManager.setLookAndFeel(cplf);

// threaded works fine and non-threaded menu text dismisses, but
gray box remains
// sometimes leads to threaded version working inconsistently and
even out
// of memeory errors
UIManager.setLookAndFeel(slf);

// threaded looks fine and non threaded menu and menu text remains
when
// selecting non-threaded
// UIManager.setLookAndFeel("com.sun.java.swing.plaf.windows.WindowsLookAndFeel
");

// threaded looks fine and non threaded menu and menu text remains
when
// selecting non-threaded
// UIManager.setLookAndFeel("com.sun.java.swing.plaf.motif.MetalLookAndFeel");

// threaded looks fine and non threaded menu and menu text remains
when
// selecting non-threaded
// UIManager.setLookAndFeel("com.sun.java.swing.plaf.motif.MotifLookAndFeel");

// threaded looks fine and non threaded menu and menu text remains
when
// selecting non-threaded
// UIManager.setLookAndFeel("javax.swing.plaf.basic.BasicLookAndFeel");

}
catch (Exception e1) {}

JMenu menu = new JMenu("Window");
menu.setMnemonic(KeyEvent.VK_W);
JMenuItem item = null;

item = new JMenuItem("Threaded");
item.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
MyDialog d = new MyDialog("Threaded");
d.show();
d.doItThreaded();
}
});
menu.add(item);

item = new JMenuItem("Threaded Using Invoke Later");
item.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
MyDialog d = new MyDialog("Threaded Using Invoke
Later");
d.show();
d.doItThreadedLater();
}
});
menu.add(item);

menu.addSeparator();

item = new JMenuItem("Non-Threaded");
item.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
MyDialog d = new MyDialog("Non-Threaded");
d.show();
d.doItNonThreaded();
}
});
menu.add(item);

JMenuBar menuBar = new JMenuBar();
menuBar.add(menu);
setJMenuBar(menuBar);

JLabel l = new
JLabel(UIManager.getLookAndFeel().getClass().getName(),
SwingConstants.CENTER);
getContentPane().setBackground(Color.CYAN);
getContentPane().add(l,BorderLayout.CENTER);

}

public static void main(String[] args) {
final TestMenu t = new TestMenu("Menu Test App");
t.show();
}


class MyDialog extends JDialog{
JProgressBar pb;

public MyDialog(String title){
super(TestMenu.this, "Test Dialog : " + title);
setSize(400,200);
setLocation(300,300);

getContentPane().add(new JLabel("Progress:"), BorderLayout.NORTH);

pb = new JProgressBar();
pb.setPreferredSize(new Dimension(400, 50));
pb.setMaximum(MAX);
getContentPane().add(pb, BorderLayout.CENTER);

}

public void doItThreaded(){
new Thread(){
public void run(){
for(int i =0; i<= MAX ; i++){
pb.setValue(i);
}
}
}.start();
}

// MUCH slower using invoke later to update the gui than not
public void doItThreadedLater(){
new Thread(){
public void run(){
for(int i =0; i<= MAX ; i++){
final int t = i;
SwingUtilities.invokeLater( new Runnable() {
public void run() {
pb.setValue(t);
}
}
);
}
}
}.start();

}

public void doItNonThreaded(){
for(int i =0; i<= MAX ; i++){
pb.setValue(i);
}


/*
SwingUtilities.invokeLater(new Runnable() {
public void run() {
for(int i =0; i<= MAX ; i++){
pb.setValue(i);
}
}
});
*/

}
}


}
 
R

rpadev

Placing the first call to your code inside an invokeLater call might
accomplish this.

I actually tried that and it had the same results. I think it's a bug
with the Windows L&F. Did you have a chance to try it? Do you see the
gray box that I am referring to?
All of the above and more. You'd be better off picking a lower level
of granularity for updates to the progress bar. If your value changes
4 million time, you might try updating only once every 10,000
iterations or so. A lot of the performance difficulty most likely
comes from the constant flip-flop between one thread and the other
trying to use the same data structures (namely, the event queue).


Not exactly, but it does prevent a new event from being logged on the
event queue every time you increment by one. The paint events are
likely collapsed in both cases, but it's those internal events
generated by invokeLater that cause the problem.


Or reduce your updates as explained above. When you are constantly
reaching a point where you could update the GUI, it's often a good ide
to see if your update is really changing things by much before doing
so. So a general pattern would be.

private int lastUpdate;

public void update(JProgressBar b, int val)
{
final int MIN_UPDATE_DIFF = 10000;

if (val - lastUpdate >= MIN_UPDATE_DIFF)
{
SwingUtilities.invokeLater(...);
lastUpdate = val;
}
}

Thanks for the response Chris. I agree with you 100%. My example was
intentionally heavy on resources. This is not actually the application
that I need to solve the problem for. The original problem I had was
regarding the menu item (or even worse a gray box when using the Windows
L&F) staying on the screen after being clicked. The LONG process here
triggered by the menu click event is just for testing to show the
behavior seen with the menu.

After making the test I found that using invokeLater() caused my test to
run more slowly. As an aside, I wanted to know why. I think my
theories are correct here.

If I did need to update a progress bar to track the progress from 1 to
4M I would use invokeLater() as it the correct way to update the gui
from another thread and would definitly do it % 10,000 or something like
that.
 
R

rpadev

invokeLater puts the Runnable at the end of the event thread, so
your menu should close before the run in the Runnable is executed.
When it is executed, it is executed on the event thread, so if it
takes a long time
to complete, your UI will still appear to hang.
Running it on a different thread, the run is executed, of course on
the thread (not the
even thread) so your UI will not appear to hang. If your thread needs
to update the UI
when finished, you need to hook back into the event thread by either
using invokeLater
or invokeAndWait.

-Bryan


John Raggio said:
I have done some reading on this and think that I understand it. I
have a some questions regarding a test program that I wrote. It
essentially tests menu items that lauch lengthy operations. I will
paste it in below.

1) If I have an action that takes some time, is there any way to have
the menu item that was clicked be dismissed before starting the
lengthy action? Depending on the L&F in use either the menu and its
text remains after clicking or the text is removed leaving only a
gray box behind. I realize the correct thing to do is to spawn a
thread to do the work, but I'd like to cheat a bit if I could.

2) Please explain why the method doItThreadedLater() takes MUCH
longer to run than doItThreaded(). My guess is that the overhead
involved with creating the extra objects and threads takes resources
and time. I also suspect that the fact that 4M events needs to be put
on the event queuue and processed may come into play. I also suspect
that doItThreaded() will allow for paints() to be combined more
often. In this case it just seems that doing it the right way takes
much loinger to execute, but is still the way to go.

I look forward to any commments.

Thanks,
John

+++++++++++++++++++++++++++++++++



import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.HeadlessException;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;

import javax.swing.JDialog;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JProgressBar;
import javax.swing.SwingConstants;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;

/**
* @author raggioj, Feb 15, 2004
*/
public class TestMenu extends JFrame {
private static final int MAX = 4000000;
JProgressBar pb;

/**
* @throws java.awt.HeadlessException
*/
public TestMenu() throws HeadlessException {
super();
}

public TestMenu(String title){
super(title);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setSize(400,200);
String cplf = UIManager.getCrossPlatformLookAndFeelClassName();
String slf = UIManager.getSystemLookAndFeelClassName();

try {

// threaded looks fine and non threaded menu and menu text remains
when
// selecting non-threaded
// UIManager.setLookAndFeel(cplf);

// threaded works fine and non-threaded menu text dismisses, but
gray box remains
// sometimes leads to threaded version working inconsistently and
even out
// of memeory errors
UIManager.setLookAndFeel(slf);

// threaded looks fine and non threaded menu and menu text remains
when
// selecting non-threaded
// UIManager.setLookAndFeel
("com.sun.java.swing.plaf.windows.WindowsLookAn
dFeel ");
// threaded looks fine and non threaded menu and menu text remains
when
// selecting non-threaded
// UIManager.setLookAndFeel
("com.sun.java.swing.plaf.motif.MetalLookAndFee
l");

// threaded looks fine and non threaded menu and menu text remains
when
// selecting non-threaded
// UIManager.setLookAndFeel
("com.sun.java.swing.plaf.motif.MotifLookAndFee
l");

// threaded looks fine and non threaded menu and menu text remains
when
// selecting non-threaded
//
UIManager.setLookAndFeel("javax.swing.plaf.basic.BasicLookAndFeel");

}
catch (Exception e1) {}

JMenu menu = new JMenu("Window");
menu.setMnemonic(KeyEvent.VK_W);
JMenuItem item = null;

item = new JMenuItem("Threaded");
item.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
MyDialog d = new MyDialog("Threaded");
d.show();
d.doItThreaded();
}
});
menu.add(item);

item = new JMenuItem("Threaded Using Invoke Later");
item.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
MyDialog d = new MyDialog("Threaded Using Invoke
Later");
d.show();
d.doItThreadedLater();
}
});
menu.add(item);

menu.addSeparator();

item = new JMenuItem("Non-Threaded");
item.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
MyDialog d = new MyDialog("Non-Threaded");
d.show();
d.doItNonThreaded();
}
});
menu.add(item);

JMenuBar menuBar = new JMenuBar();
menuBar.add(menu);
setJMenuBar(menuBar);

JLabel l = new
JLabel(UIManager.getLookAndFeel().getClass().getName(),
SwingConstants.CENTER);
getContentPane().setBackground(Color.CYAN);
getContentPane().add(l,BorderLayout.CENTER);

}

public static void main(String[] args) {
final TestMenu t = new TestMenu("Menu Test App");
t.show();
}


class MyDialog extends JDialog{
JProgressBar pb;

public MyDialog(String title){
super(TestMenu.this, "Test Dialog : " + title);
setSize(400,200);
setLocation(300,300);

getContentPane().add(new JLabel("Progress:"), BorderLayout.NORTH);

pb = new JProgressBar();
pb.setPreferredSize(new Dimension(400, 50));
pb.setMaximum(MAX);
getContentPane().add(pb, BorderLayout.CENTER);

}

public void doItThreaded(){
new Thread(){
public void run(){
for(int i =0; i<= MAX ; i++){
pb.setValue(i);
}
}
}.start();
}

// MUCH slower using invoke later to update the gui than not
public void doItThreadedLater(){
new Thread(){
public void run(){
for(int i =0; i<= MAX ; i++){
final int t = i;
SwingUtilities.invokeLater( new Runnable() {
public void run() {
pb.setValue(t);
}
}
);
}
}
}.start();

}

public void doItNonThreaded(){
for(int i =0; i<= MAX ; i++){
pb.setValue(i);
}


/*
SwingUtilities.invokeLater(new Runnable() {
public void run() {
for(int i =0; i<= MAX ; i++){
pb.setValue(i);
}
}
});
*/

}
}


}

Bryan:

Thanks for the response. Please see my response to Chris' post above.
I agree with all that you are saying here. I know this is the way to do
it correctly, but I just wanted to know why the correct way to do it
seemed to be MUCH slower. I think the reponses here agree with the
thoery that I posted originally. I just wanted to verify my theory.

BTW, if I used invokeLater() to open the dialog, the menu item still
remained on the screen. Perhaps a paint didn't have a chance to fire in
time before the invokeLater runnable began to excute on the event
thread? No matter what I tried, the menu item remained on screen after
clicking unless I did the big loop in a separate thread. I assumed like
you that the runnable would be placed at the end of the event queue and
that the menu would then clear itself off the screen because its event
handler would be finished. No such luck. Did you get a chance to run
this by any chance? I'd be curious if you saw the same behavior I did.
Recall that using Windows L&F I see a gray box where the menu dropped
down and under metal L&F the menu remains including the text after
clicking.

Thanks
John
 
A

Andrew Harker

rpadev said:
I actually tried that and it had the same results. I think it's a bug
with the Windows L&F. Did you have a chance to try it? Do you see the
gray box that I am referring to?
Thanks for the response Chris. I agree with you 100%. My example was
intentionally heavy on resources. This is not actually the application
that I need to solve the problem for. The original problem I had was
regarding the menu item (or even worse a gray box when using the Windows
L&F) staying on the screen after being clicked. The LONG process here
triggered by the menu click event is just for testing to show the
behavior seen with the menu.
Having another thread do some heavy/long (non-gui) work is the right
thing to do and as you see the menu disappears as the gui event thread
gets some time. (Although your mileage may vary on different machines
and underlying OS)

Maybe you should look at the other options for despatching your work,
eg SwingWorker utility class - see
http://java.sun.com/docs/books/tutorial/uiswing/misc/threads.html

Also different ways to monitor progress, eg ProcessMonitor or use the
indeterminate mode in JProgressBar
After making the test I found that using invokeLater() caused my test to
run more slowly. As an aside, I wanted to know why. I think my
theories are correct here.

If I did need to update a progress bar to track the progress from 1 to
4M I would use invokeLater() as it the correct way to update the gui
from another thread and would definitly do it % 10,000 or something like
that.

I think so too - 4M updates each one creating a new runnable plus
context switching! If you take out the progress bar updates it works
a lot better :) I tried your code and saw the same as you - moving
to %10000 made a huge difference.

Is it that your actual (non-gui) work takes a _lot_ longer if put
into a thread or only if you are using the progress bar monitoring?
 
J

John Raggio

<SNIPPED>

Andrew:

Thanks for the reply. The non gui works takes a few seconds. I was
just looking for a way to get rid of the gray box or the menu after it
was clicked even if the gui was busy doing the 2 second job. We put
up an hourglass cursor while priming the child dialog and displayig
it, but I just hate the gray box left behind after clicking the menu!
It looks like the only way to avoid it is to build the child in
another thread so the pareent dialog can repaint correctly removing
the menu etc. The gray box gives the illusion of a clunky gui to me.

My sample code was built to demonstrate the gray box behavior, but
then it showed me that the overhead of invokeLater() was substantial.
This behavior wqs exagerated because of my 4M loop.

It all makes sense. I just wanted to be sure that I understood why.
 
D

Dale King

The original message did not show up on my newsfeed, so I will piggy back on
Chris' response


I was always told tat cheaters never win and winners never cheat ;-). That
is an old adage but it is true.
Placing the first call to your code inside an invokeLater call might
accomplish this.

Not likely. invokeLater really makes little difference here.

There is a single thread called the event-dispatch thread that handles both
dispatch of events and repainting. The basic rule here is that if you do any
lengthy operations on this thread then you will slow down the drawing and
responsiveness of your GUI. It is because you are doing operations on this
thread that you are delaying the repaint that "removes" the displayed menu.

Using invokeLater does not cause the operation to execute on a separate
thread from the event dispatch thread. The purpose of invokeLater is
actually to guarantee that the operation *is* run on the event-dispatch
thread. Most of the AWT and Swing components are not guaranteed to be thread
safe. If you invoke a method on a Swing component that changes its state
from another thread and the event dispatch thread is currently painting that
component there is no guarantee what may happen. For example, if you changed
the color of a component while that component was being painted you might
end up with half of it painted in one color and half in another color. In
the extreme case you might even cause a deadlock. There are only a handful
of methods that are guaranteed to be safe to call from any thread (repaint,
invalidate, revalidate, add/remove listeners being the prime examples).

The purpose of invokeLater is to make sure that the operation is done on the
event-dispatch thread such that neither an event dispatch nor a repaint is
currently taking place. In this case it buys you nothing because you are
already on the event dispatch thread.

The only way to solve this is to do it correctly and use separate threads to
do the lengthy operations. Sun has developed a utility class for doing this
called SwingWorker. I suggest reading:

http://java.sun.com/docs/books/tutorial/uiswing/misc/threads.html

And there are several articles on the topic here:

http://java.sun.com/products/jfc/tsc/articles/index.html

It is not the extra threads since there are only two threads. It has much
more to do with all the objects and garbage collection being done. Try
running your app with -verbose:gc turned on and it should shed some light on
the subject.

You are queueing up Runnables to be invoked later. That queue is only
emptied when the event-dispatch thread has a chance to run. But because you
are hogging the processor the event dispatch only gets to run when it gets a
time slice. And you are putting items in the queue a whole lot faster than
the event dispatch thread can empty them. So the queue is simply going to
keep growing and growing consuming more memory requiring more and more GC to
keep up.

So first let's attack the creation of so many objects. You are creating 4M
runnables the job of each is exactly the same, to update the ProgressBar.
Each one updates it to a different value. But that isn't really what you
want. You really want it to always update to the current amount of progress,
not to the amount of progress when the Runnable was created. We could have
done a lot more work since then. So by making t an instance variable instead
of a final local variable you can get by with only creating a single
Runnable instance:

public void doItThreadedLater()
{
new Thread()
{
private volatile int t;

public void run()
{
Runnable r = new Runnable()
{
public void run()
{
pb.setValue(t);
}
};

for( int i =0; i <= MAX; i++ )
{
t = i;
SwingUtilities.invokeLater( r );
}
}
}.start();
}

While that is a dramtic improvement, we still aren't done. If we already
have queued up a runnable instance and it hasn't executed yet, there really
isn't a need to queue up another that is going to do exactly the same thing.
You are queueing up unnecessary work. It make much more sense to simply
maintain a queue of only one instance and only queue up another when the
last one has started. This can be done fairly easily yielding a very fast
version of the loop:

public void doItThreadedLater()
{
new Thread()
{
private volatile int t;
private volatile boolean queueEmpty = true;

public void run()
{
Runnable r = new Runnable()
{
public void run()
{
queueEmpty = true;
pb.setValue(t);
}
};

for( int i =0; i <= MAX; i++ )
{
t = i;
if( queueEmpty )
{
queueEmpty = false;
SwingUtilities.invokeLater( r );
}
}
}
}.start();
}

In reality, I think an expert in Java multi-threaded programming would
probably tell you that Java's memory model does not guarantee that this will
work correctly because you have variables modified on one thread being read
on a different thread and no synchronization (see any of the million
discussions about why double-checked locking does not work). The values
written may not be immediately be visible on the other thread. You could end
up completing the work put the ProgressBar not updated to reflect that. I
made the variables volatile, but I believe that for this to really be
guaranteed to work correctly you should add synchronization, but that slows
it back down. You might then want to apply the technique Chris talks about
to limit the updates:
Or reduce your updates as explained above. When you are constantly
reaching a point where you could update the GUI, it's often a good ide
to see if your update is really changing things by much before doing so.
So a general pattern would be.

private int lastUpdate;

public void update(JProgressBar b, int val)
{
final int MIN_UPDATE_DIFF = 10000;

if (val - lastUpdate >= MIN_UPDATE_DIFF)
{
SwingUtilities.invokeLater(...);
lastUpdate = val;
}
}

That is exactly what the ProgressMonitor class does. It sets the minimum
delta to ( min - max ) / 100. What I find interesting about ProgressMonitor
though is that it does not use the invokeLater. So I'm wondering if setValue
is one of those methods that is allowed from another thread, but it is not
marked that way and I don't believe it is.

So here is a final version that combines everything talked about here and it
runs very fast.

public void doItThreadedLater()
{
new Thread()
{
private volatile int t;
private volatile boolean queueEmpty = true;

public void run()
{
Runnable r = new Runnable()
{
public void run()
{
synchronized( this )
{
queueEmpty = true;
pb.setValue(t);
}
}
};

for( int i =0; i <= MAX; i++ )
{
if( i - t > MAX / 100 )
{
synchronized( r )
{
t = i;
if( queueEmpty )
{
queueEmpty = false;
SwingUtilities.invokeLater( r );
}
}
}
}

// Done make sure we update to max
synchronized( r )
{
t = MAX;
SwingUtilities.invokeLater( r );
}
}
}.start();
}
 
R

rpadev

The original message did not show up on my newsfeed, so I will piggy
back on Chris' response



I was always told tat cheaters never win and winners never cheat ;-).
That is an old adage but it is true.


Not likely. invokeLater really makes little difference here.

There is a single thread called the event-dispatch thread that handles
both dispatch of events and repainting. The basic rule here is that if
you do any lengthy operations on this thread then you will slow down
the drawing and responsiveness of your GUI. It is because you are
doing operations on this thread that you are delaying the repaint that
"removes" the displayed menu.

Using invokeLater does not cause the operation to execute on a
separate thread from the event dispatch thread. The purpose of
invokeLater is actually to guarantee that the operation *is* run on
the event-dispatch thread. Most of the AWT and Swing components are
not guaranteed to be thread safe. If you invoke a method on a Swing
component that changes its state from another thread and the event
dispatch thread is currently painting that component there is no
guarantee what may happen. For example, if you changed the color of a
component while that component was being painted you might end up with
half of it painted in one color and half in another color. In the
extreme case you might even cause a deadlock. There are only a handful
of methods that are guaranteed to be safe to call from any thread
(repaint, invalidate, revalidate, add/remove listeners being the prime
examples).

The purpose of invokeLater is to make sure that the operation is done
on the event-dispatch thread such that neither an event dispatch nor a
repaint is currently taking place. In this case it buys you nothing
because you are already on the event dispatch thread.

The only way to solve this is to do it correctly and use separate
threads to do the lengthy operations. Sun has developed a utility
class for doing this called SwingWorker. I suggest reading:

http://java.sun.com/docs/books/tutorial/uiswing/misc/threads.html

And there are several articles on the topic here:

http://java.sun.com/products/jfc/tsc/articles/index.html


It is not the extra threads since there are only two threads. It has
much more to do with all the objects and garbage collection being
done. Try running your app with -verbose:gc turned on and it should
shed some light on the subject.

You are queueing up Runnables to be invoked later. That queue is only
emptied when the event-dispatch thread has a chance to run. But
because you are hogging the processor the event dispatch only gets to
run when it gets a time slice. And you are putting items in the queue
a whole lot faster than the event dispatch thread can empty them. So
the queue is simply going to keep growing and growing consuming more
memory requiring more and more GC to keep up.

So first let's attack the creation of so many objects. You are
creating 4M runnables the job of each is exactly the same, to update
the ProgressBar. Each one updates it to a different value. But that
isn't really what you want. You really want it to always update to the
current amount of progress, not to the amount of progress when the
Runnable was created. We could have done a lot more work since then.
So by making t an instance variable instead of a final local variable
you can get by with only creating a single Runnable instance:

public void doItThreadedLater()
{
new Thread()
{
private volatile int t;

public void run()
{
Runnable r = new Runnable()
{
public void run()
{
pb.setValue(t);
}
};

for( int i =0; i <= MAX; i++ )
{
t = i;
SwingUtilities.invokeLater( r );
}
}
}.start();
}

While that is a dramtic improvement, we still aren't done. If we
already have queued up a runnable instance and it hasn't executed yet,
there really isn't a need to queue up another that is going to do
exactly the same thing. You are queueing up unnecessary work. It make
much more sense to simply maintain a queue of only one instance and
only queue up another when the last one has started. This can be done
fairly easily yielding a very fast version of the loop:

public void doItThreadedLater()
{
new Thread()
{
private volatile int t;
private volatile boolean queueEmpty = true;

public void run()
{
Runnable r = new Runnable()
{
public void run()
{
queueEmpty = true;
pb.setValue(t);
}
};

for( int i =0; i <= MAX; i++ )
{
t = i;
if( queueEmpty )
{
queueEmpty = false;
SwingUtilities.invokeLater( r );
}
}
}
}.start();
}

In reality, I think an expert in Java multi-threaded programming would
probably tell you that Java's memory model does not guarantee that
this will work correctly because you have variables modified on one
thread being read on a different thread and no synchronization (see
any of the million discussions about why double-checked locking does
not work). The values written may not be immediately be visible on the
other thread. You could end up completing the work put the ProgressBar
not updated to reflect that. I made the variables volatile, but I
believe that for this to really be guaranteed to work correctly you
should add synchronization, but that slows it back down. You might
then want to apply the technique Chris talks about to limit the
updates:


That is exactly what the ProgressMonitor class does. It sets the
minimum delta to ( min - max ) / 100. What I find interesting about
ProgressMonitor though is that it does not use the invokeLater. So I'm
wondering if setValue is one of those methods that is allowed from
another thread, but it is not marked that way and I don't believe it
is.

So here is a final version that combines everything talked about here
and it runs very fast.

public void doItThreadedLater()
{
new Thread()
{
private volatile int t;
private volatile boolean queueEmpty = true;

public void run()
{
Runnable r = new Runnable()
{
public void run()
{
synchronized( this )
{
queueEmpty = true;
pb.setValue(t);
}
}
};

for( int i =0; i <= MAX; i++ )
{
if( i - t > MAX / 100 )
{
synchronized( r )
{
t = i;
if( queueEmpty )
{
queueEmpty = false;
SwingUtilities.invokeLater( r );
}
}
}
}

// Done make sure we update to max
synchronized( r )
{
t = MAX;
SwingUtilities.invokeLater( r );
}
}
}.start();
}

Wow Dale. Thanks for the response. I appreciate the effort here. My
orignal post had to do with the gray box being left behind.

The idea to use invokeLater() without using a thread for the lengthy op
seemed to make sense to me in this case to get rid of the menu item
after clicking, but it did not work I know that the gui would be frozen
if I did all the work on the event thread, but I would be willing to sdo
this in my "real app" if I could figure out how to get the menu item to
be dismissed when clicked. My app needs about 2 seconds to prime a
second dialog that gets shown when the menu item is clicked.

I figured

1) the event handler for the menu item would be fired
2) It would add a new process to the event queue to open the 2nd dialog
using invokeLater
3) The menu event handler would end
4) The menu would be "erased"
5) The process called in #2 would begin

But this is not what happened.

Then I built this test program as a eans of understanding this all and
then found that invokeLater() causeda major slowdown. I agree with your
suggestions and the others posted before. It doesn't make a lot of
sense to update the progress bar as often. This was purposely done to
create a "busy" process.

Thanks to all,
John
 
B

Bryan E. Boone

Your menu event handler does end if you use invokeLater.
If the processing you do in the run of the Runnable (used in invokeLater)
takes a lot of time, your still hanging the UI.

-Bryan
 
R

rpadev

Your menu event handler does end if you use invokeLater.
If the processing you do in the run of the Runnable (used in
invokeLater) takes a lot of time, your still hanging the UI.

-Bryan

The event handler ends, but the menu does not repaint. At least it
didn't when I was testing. Could it be that the paint did not make it
onto the event queue before the secondary window was opened.

Thanks,
John
 
B

Bryan E. Boone

No, in my experience, the paint will make it (it's just low priority).
In the past, I'd goober things up by calling
paint(...)
paintImmediately()
invalidate/validate,
and all other gyrations here
to get the UI to repaint, when all along it was something I was doing
that wasn't playing nice with Swing/AWT
BTW.... repaint() was _always_ the answer. (AFAIK)

For better or worse, Sun does a pretty good job with
thier stuf (80% of the time anyway). If I see flicker, lag,
or some other UI anomoly I usually consider it _my_ bug first, then point
at Sun.

I also keep a copy of the jdk source handy so I can investigate exactly
what they (Sun) are doing in the code.

Without compiling/runinnging your code snippet, I'd think that your Runnable
is taking a long time and since you are putting it on the EventQueue, you're
still
holding up the UI.

In rare occasions, the panel doesn't know that it needs a repaint, but it
does happen.
When it does happen, usually it's in JScrollPane or in JPanel where things
make heavy
use of double bufferring. (which is built into Swing)

-Bryan
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,774
Messages
2,569,596
Members
45,143
Latest member
SterlingLa
Top