How does ignoring the event dispatch thread allow this program to work?
Asked Answered
P

2

7

As I tried to see if I could answer this question earlier today. I realized that I don't fully understand the Event Dispatch Thread (EDT). Googling both confirmed and helped with that and clarified why I don't. (This might also be relevant to understanding.)

The code sets up a GUI and later (as in the earlier question) updates a text field until a flag is unset.

I have several questions/requests.

  • Please explain why the code below runs fine if both calls (to swingInit and doIt) are outside the invokeLater block (as shown), since both calls affect or query the GUI yet neither are executing on the EDT (are they?). Isn't that inviting failure?

  • Code also runs if call to swingInit is inside and doIt outside invokeLater. So swingInit is executed on the EDT, but shouldn't doIt not executing on the EDT be a problem? (I was surprised that this worked. Should I have been?)

  • I guess I understand why it hangs if doIt is inside invokeLater regardless of where swingInit is: the purpose of invokeLater is ONLY to initialize the GUI (right?).

  • Should doIt only be initiated (possibly from an event occurring) on the EDT but certainly not inside invokeLater block?

(The history of the EDT concept is interesting. It was not always thus. See link above to "why I don't" understand it.)

import static java.awt.EventQueue.invokeLater;
import java.awt.event.*;
import javax.swing.*;

public class Whatever 
{
  static boolean flag = true;
  static JTextField tf = new JTextField("Hi",20);
  static JPanel p = new JPanel();
  static JFrame f = new JFrame();
  static JButton b = new JButton("End");

  public static void main(String[] args)
  {
    swingInit();

    invokeLater
    (
      new Runnable()
      {
        @Override
        public void run() 
        {
    //    swingInit();
    //    doIt();
        }
      }
    ); 
   doIt();      
  } 

  static void swingInit()
  {
     b.addMouseListener
    (
        new MouseAdapter()
        {
          @Override
          public void mouseClicked(MouseEvent e)
          {
            flag = false;
            JOptionPane.showMessageDialog(null,"Clicked... exiting");
            System.exit(0);
          }
        }
    );

    p.add(tf);
    p.add(b);
    f.add(p);
    f.setVisible(true);
    f.pack();
    f.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
  }

  static String getInfo(){
    return "Hello... " + Math.random();
  }

  static void doIt(){
    while(flag)     
      tf.setText(getInfo());
  }; 
}
Pickel answered 8/5, 2015 at 19:28 Comment(1)
As I think you understand, you should not update the GUI from threads other than the EDT - however, doing so doesn't necessarily fail - it's just very risky. So don't be surprised if such code sometimes seems to work.Casa
W
3

Taking each of your bullet points:

  • The code is launched on the Main thread - the EDT is run in parallel. swingInit returns after constructing the UI which is then under control of the EDT, allowing dotIt to do its thing in parallel on the main thread

  • Similar situation as above, but here you are guaranteeing construction of the UI on the EDT (as recommended by Oracle).

  • A long running task is placed onto the EDT, preventing it from showing (if placed before swingIt) or painting and interaction (if placed after). the purpose of invokeLater is ONLY to initialize the GUI The purpose is to place non-thread safe Swing calls onto the EDT. If within the main method, I would recommend using SwingUtilities.invokeAndWait

  • If you wish to update the UI like this, consider doing so with a SwingTimer.

Running EDT specific, non-thread safe code outside the EDT doesn't guarantee failure, but it does invite failure (via conflicts when two (or more) threads attempt to update data at the same time).

I once spent hours tracking down a mysterious NullPointerException, only to realize it was a LookAndFeel issue whose calls were not on the EDT. Lesson learned.

Warfare answered 8/5, 2015 at 19:46 Comment(2)
@copeg--Thanks for the thorough answer. Just one question: Shouldn't "guaranteeing construction of the UI on the EDT (good practice)" say " .. on the EDT (as virtually required by Sun/Java edict about Swing not being thread safe)"?Pickel
You're welcome - I hope it clarifies things for you...answer edited based upon your recommendationWarfare
R
2
static void doIt(){
  while(flag)     
    tf.setText(getInfo());
}; 

That busy-loop inside doIt ties up the GUI thread (spinning in that loop), which will cause the GUI to hang.

You didn't actually explain what you mean by "runs fine", but I'm assuming that's the problem that you're seeing.

You might want to use a Swing Timer to do something like what you do in the loop.

Riles answered 8/5, 2015 at 19:35 Comment(1)
Thanks. "Runs fine" means that the text field is continually updated "as expected" with visible changing results until the "End" button is clicked. Code should run as is if you want to see what happens.Pickel

© 2022 - 2024 — McMap. All rights reserved.