views:

76

answers:

3

I read that all the code which constructs Swing components and handles Events must be run by the Event Dispatch Thread. I understand how this is accomplished by using the SwingUtilities.invokeLater() method. Consider the following code where the GUI initialization is done in the main method itself

public class GridBagLayoutTester
        extends JPanel implements ActionListener
{   
    public GridBagLayoutTester()
    {
        setLayout(new GridBagLayout());
        GridBagConstraints gbc = new GridBagConstraints();

        JButton button = new JButton("Testing");
        gbc.fill = GridBagConstraints.HORIZONTAL;
        gbc.anchor = GridBagConstraints.WEST;
        gbc.gridx = 0;
        gbc.gridy = 0;
        gbc.gridwidth = 1;
        button.addActionListener(this);
        add(button, gbc);
    }

    public void actionPerformed(ActionEvent e)
    {
        System.out.println("event handler code");
    }

    public static void main(String[] args)
    {
        JFrame frame = new JFrame("GridBagLayoutDemo");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);    
        Container contentPane = frame.getContentPane();
        contentPane.setLayout(new BorderLayout());
        contentPane.add(new GridBagLayoutTester(), BorderLayout.CENTER);
        frame.setSize(800, 600);
        frame.pack();
        frame.setVisible(true);
        System.out.println("Exiting");
    }   
}

How is it that this code works perfectly ? We are constructing JFrame and calling a host of other methods in the main thread. I do not understand where exactly the EDT is coming into picture here (what code is it executing ?). The constructor of the GridBagLayoutTester class is also being called from the main method which means the EDT is not running it.

In short

  1. When is the EDT being started ? (does the JVM start the EDT along with the main method if at all the EDT is started while running this code ?)
  2. Does the event handler code for the button run on the EDT ?
+1  A: 

1) I don't know if in new JFrame or in setVisible but it initialize on demand and that's what the end of the main method (over the main process thread) doesn't terminate the process. the EDT was launched and is in a loop blocked waiting the next event.

2) Definitively. That loop receives from the OS the event, find the JButton and tells it that the event was fired. The button then calls the listeners. All that happens in the EDT.

You could review the Swing code that you call when you want to kill the process (or closing the main window) for looking where the EDT is terminated... that can give you a clue (I'll do it later! :)

helios
+2  A: 

The Event Dispatcher Thread, as it name implies, is call by Swing every time an event needs to be processed.

In the example you gave, the "Testing" button will automatically call the actionPerformed method when an action event needs to be processed. So, the content of your actionPerformed method will be invoked by the Event Dispatcher Thread.

To answer your two final questions:

  • The EDT is started automatically when the Swing framework loads. You don't have to care about starting this thread, the JRE handles this task for you.
  • The event handler code is run by the EDT. All events your Swing interface generates are pooled and the EDT is responsible for executing them.
Vivien Barousse
+5  A: 

The code works perfectly because you are constructing the frame in the main thread, before the EDT has an opportunity to interact with it. Technically, you shouldn't do this ever, but technically you can under this specific circumstance because you cannot interact with the JFrame until it becomes visible.

The main point to know is that Swing components are not thread safe. This means that they cannot be modified from more than one thread at the same time. This is solved by ensuring that all modifications come from the EDT.

The EDT is a thread that's dedicated to user interaction. Any events generated from the user are always run on the EDT. Any user interface updates run on the EDT. For example, when you call Component.repaint(), you can call this from any thread. This simply sets a flag to mark the component as needing a paint, and the EDT does it on its next cycle.

The EDT is started automatically and is tied quite closely into the system implementation. It is handled well within the JVM. Typically, it correlates to a single thread in the windowing system that handles user interaction. Of course, this is quite implementation-dependent. The nice thing is that you don't have to worry about this. You just have to know - if you interact with any Swing components, do it on the EDT.

Likewise, there is one other thing that's important. If you are going to do any long-duration processing or blocking for an external resource, and you are going to do it in response to an event generated by the user, you have to schedule this to run in its own thread off the EDT. If you fail to do this, you will cause the user interface to block while it waits for the long-duration processing to run. Excellent examples are loading from files, reading from a database, or interacting with the network. You can test to see if you are on the EDT (useful for creating neutral methods which can be called from any thread) with the SwingUtilities.isEventDispatchThread() method.

Here are two snippets of code which I use quite frequently when writing Swing programming dealing with the EDT:

void executeOffEDT() {
  if (SwingUtilities.isEventDispatchThread()) {
    Runnable r = new Runnable() {
      @Override
      public void run() {
        OutsideClass.this.executeOffEDTInternal();
      }
    };
    new Thread(r).start();
  } else {
    this.executeOffEDTInternal();
  }
}

void executeOnEDT() {
  if (SwingUtilities.isEventDispatchThread()) {
    this.executeOnEDTInternal();
  } else {
    Runnable r = new Runnable() {
      @Override
      public void run() {
        OutsideClass.this.executeOnEDTInternal();
      }
    };
    SwingUtilities.invokeLater(r);
  }
}
Erick Robertson
Regarding Component.repaint() I doubt this only "sets a flag", it actually queues a painting event (that will then be processed by the EDT).
jfpoilpret
The pattern is the same. You don't need to know the internals of the way this is handled in order to use the EDT successfully.
Erick Robertson
so the frame.setVisible() call is executed on the EDT ?
Stormshadow
No. In your example, the whole main() method is executed on the main thread. The only line of code executed on the EDT is in the actionPerformed method. The standard that I've seen is to create some sort of initializeUserInterface() method, and wrap it in a runnable and call invokelater on it from the main thread. Then you move the entire contents of the main method in there. That's the 100% proper way to handle it.
Erick Robertson
@Erick so basically Swing components can indeed be constructed and rendered from the main thread though it is best to move this code to the EDT.
Stormshadow
Yes. If you try to do it before it's visible on the screen, then it won't break because nobody can be interacting with it while you're setting it up. If you try to do it after it's visible on the screen, you run the risk of throwing concurrency exceptions.
Erick Robertson