When you use AWT or Swing, there is a special thread running in the background, called the EventQueue. When you add a listener to a component (such as an ActionListener to a JButton) and the event listener method (such as actionPerformed) the method is executed on the EventQueue thread. You can choose to execute something on the EventQueue thread when the current thread isn't the EventQueue thread by running
EventQueue.invokeLater(new Runnable(){ //this could also be SwingUtilities.invokeLater() instead
public void run(){
...
}
});
This is great. The problem comes when you have a an event over and over and over. When this happens, the EventQueue thread can't execute code for the previous event fast enough to execute code for the next event. So it sticks the event into a queue while it waits for the current event-handler to finish. This might happen when you do a big operation, such as reading from a file, on the EventQueue thread. For example:
ActionListener l = new ActionListener(){
public void actionPerformed(ActionEvent ae){
try {
BufferedReader in = new BufferedReader(new FileReader("foo.txt"));
StringBuffer sb = new StringBuffer();
char[] buf = new char[4096];
int n;
while (-1 != (n = in.read(buf))){
sb.append(buf, 0, n);
}
in.close();
String s = sb.toString();
jTextPane1.setText(s);
} catch (IOException ioe){
ioe.printStackTrace();
}
}
};
jButton1.addActionListener(l);
All the code inside actionPerformed
is executed on the EventQueue thread. No other events could be processed until that code completed. If foo.txt is a big file, then it will be a while before the code completes. This means that every time you click jButton1
before the code finished, it will add the new event to the EventQueue. So if you repeatedly clicked jButton1
, then there would be more and more events to add to the queue. You couldn't overflow it this way, because the you couldn't click fast enough, and for long enough without giving up. But if and event such as a mouse move, that occurs at every pixel, leads to a heavy-event. Then that can cause a huge EventQueue queue and it can even cause the EventQueue to overflow. A way to fix this problem might be to start another thread as soon as the event occurs. For example:
final Runnable r = new Runnable() {
public void run() {
try {
BufferedReader in = new BufferedReader(new FileReader("foo.txt"));
StringBuffer sb = new StringBuffer();
char[] buf = new char[4096];
int n;
while (-1 != (n = in.read(buf))) {
sb.append(buf, 0, n);
}
in.close();
final String s = sb.toString();
EventQueue.invokeLater(new Runnable(){
public void run(){
jTextPane1.setText(s);
}
});
} catch (IOException ioe) {
ioe.printStackTrace();
}
}
};
ActionListener l = new ActionListener(){
public void actionPerformed(ActionEvent ae){
new Thread(r).start();
}
};
jButton1.addActionListener(l);
The reason it would be necessary to invoke something on the EventQueue thread, such as in
EventQueue.invokeLater(new Runnable(){
public void run(){
jTextPane1.setText(s);
}
});
Is because otherwise the screen wouldn't be updated. For example, if maintaining a progress bar, EventQueue.invokeLater()
would update the screen NOW, but if it isn't wrapped in EventQueue.invokeLater()
, then it will wait until the thread finishes before it updates the screen. That is useless when it comes to progress bars. In this case it probably didn't do much.
Thanx for a great question!