Per il seguente esempio di programma GUI utilizzando javax.swing,
public class UnResponsiveUI extends JFrame{
private boolean stop = false;
private JTextField tfCount;
private int count = 1;
/* Constructor to setup the GUI components */
public UnResponsiveUI(){
Container cp = this.getContentPane();
cp.setLayout(new FlowLayout(FlowLayout.CENTER, 10, 10));
cp.add(new JLabel("Counter"));
tfCount = new JTextField(count + "", 10);
tfCount.setEditable(false);
cp.add(tfCount);
JButton btnStart = new JButton("Start Counting");
cp.add(btnStart);
btnStart.addActionListener(new ActionListener(){
@Override
public void actionPerformed(ActionEvent e) {
stop = false;
for (int i = 0; i < 100000; ++i) {
if (stop) break; // check if STOP button has been pushed,
// which changes the stop flag to true
tfCount.setText(count + "");
++count;
}
}
});
JButton btnStop = new JButton("Stop Counting");
cp.add(btnStop);
btnStop.addActionListener(new ActionListener(){
@Override
public void actionPerformed(ActionEvent evt) {
stop = true; // set the stop flag
}
});
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setTitle("Counter");
setSize(300, 120);
setVisible(true);
}
public static void main(String[] args){
/*SwingUtilities.invokeLater(new Runnable(){
public void run() {
new UnResponsiveUI(); // Let the constructor do the job
}
});*/
/* new UnResponsiveUI(); */
System.out.println("main thread exits");
}
}
Nel primo scenario, dopo aver eseguito il debug del costruttore richiamato da main () come new UnResponsiveUI();
, di seguito viene riportata l'osservazione del numero di thread avviati:
1. The main() method starts in the "main" thread.
2. A new thread "AWT-Windows" (Daemon thread) is started when we step-into the constructor "new UnresponsiveUI()" (because of the "extends JFrame").
3. After executing "setVisible(true)", another two threads are created - "AWT-Shutdown" and "AWT-EventQueue-0" (i.e., the EDT).
4. The "main" thread exits after the main() method completes. A new thread called "DestroyJavaVM" is created.
5. At this point, there are 4 threads running - "AWT-Windows", "AWT-Shutdown" and "AWT-EventQueue-0 (EDT)" and "DestroyJavaVM".
6. Clicking the START button invokes the actionPerformed() in the EDT.
Nel secondo scenario, dopo il debug del costruttore da main () invocato come,
SwingUtilities.invokeLater(new Runnable(){
public void run() {
new UnResponsiveUI(); // Let the constructor do the job
}
});
Segue l'osservazione per il numero di thread che vengono lanciati ma in istanze temporali diverse:
1. The main() method is started in the "main" thread.
2. The JRE's windowing subsystem, via
SwingUtilities.invokeLater()
, starts 3 threads at a time: "AWT-Windows" (daemon thread), "AWT-Shutdown" and "AWT-EventQueue-0". The "AWT-EventQueue-0" is known as the Event-Dispatching Thread (EDT), which is the one and only thread responsible for handling all the events (such as clicking of buttons) and refreshing the display to ensure thread safety in GUI operations and manipulating GUI components.3. Then we step-into the constructor
UnresponsiveUI()
to run on the Event-Dispatching thread (created viainvokeLater()
), after all the existing events have been processed.4. The "main" thread exits after the main() method completes.
5. A new thread called "DestroyJavaVM" is created.
Quindi, in due scenari sopra, il numero totale di thread da creare è lo stesso, ma in istanze temporali diverse ed eseguire il costruttore della GUI sullo stesso EDT.
Da questa query , ho appreso che: "Se non hai usato invokeLater e invece hai appena aggiornato l'ui direttamente potresti avere una race condition e potrebbe verificarsi un comportamento indefinito. "
La mia domanda:
Qual è il vantaggio dell'utilizzo del metodo SwingUtilities.invokeLater()
? Perché in un secondo scenario, l'avvio di EDT prima dell'ingresso nel costruttore non ha aggiunto alcun valore.