5

Is Java capable of creating more than one EDT at a time?

I'm experimenting with setting up EDT and how it works in updating the content of a "heavy duty" panel with potentially a dozen of panels embedded inside and with hundreds of components altogether. Currently I have

        public void run() {
            SwingUtilities.invokeLater(new Runnable() {
                public void run() {
                    panel.update();
                }
            });
        }

I've looked at the following posts:

Measuring "busyness" of the event dispatching thread

How does the event dispatch thread work?

Java Event-Dispatching Thread explanation

http://en.wiki2.org/wiki/Event_dispatching_thread

and so forth.

I sort of understand that if there are, say a dozen of events, that an single EDT has to handle, Java already has an internal scheduling mechanism to group/prioritize these events.

According to http://docs.oracle.com/javase/tutorial/uiswing/concurrency/dispatch.html

"This is necessary because most Swing object methods are not "thread safe": invoking them from multiple threads risks thread interference or memory consistency errors."

So what if I create a 2nd EDT with new Thread(new Runnable() { ... }.start() below?

Will java automatically merge the two EDTs back to one for fear of thread safety?

       new Thread(new Runnable() {
        public void run() {
            SwingUtilities.invokeLater(new Runnable() {
                public void run() {
                    panel.update();
                }
            });
        }
    }).start();
Community
  • 1
  • 1
mk7
  • 150
  • 1
  • 9
  • 3
    Your second thread is not EDT, just T. ;) – Gábor Bakos Jan 07 '15 at 15:42
  • 2
    I don't think you can create an EDT. Swing creates its own EDT. You can create as many threads as you like (up to some huge limit), but they won't be EDT's. Your example code creates a `Runnable` object, but it won't run `panel.update` in your thread. Instead, this object is given to Swing, which saves it somewhere, and then eventually the EDT finds it and runs it. – ajb Jan 07 '15 at 15:43
  • possible duplicate of [Multiple Event Dispatch Threads](http://stackoverflow.com/questions/7323306/multiple-event-dispatch-threads) – Jayan Jan 07 '15 at 16:10

3 Answers3

2

There can only be one Event Dispatch Thread!

But why would you even want to have more than one thread for this? Even for "heavy duty" panels with many components (in the application i am currently working on there must be 1000s of components) one EDT is enough. Remember you should not perform any tasks on the EDT that use a lot of CPU time. Otherwise you will block the EDT for update events and your GUI will become "sluggish" in responding to user input.

Also remember that all GUI components should be created and manipulated only from within the EDT because many components are not thread save. Ignoring this guideline may work for specific tasks but sooner or later you will get strange behavior and/or crashes!

jo-
  • 234
  • 1
  • 9
  • 1
    There are computations that I've done within a class that's also responsible for creating a panel and components. so I suppose it's a good practice for me to separate the core logic from the GUI into different class – mk7 Jan 07 '15 at 16:07
  • Correct! In general you want to have a look at the [Model-View-Controller](http://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93controller) pattern.Also remember to always ensure that creation and manipulation of GUI components are executed on the EDT. Most times this is automatically the case as you call the code from within e.g. an ActionListener (which is run on the EDT) but sometimes you want to access GUI elements form a different thread an then you will have to use SwingUtilities.invokeLater or SwingUtilities.invokeAndWait. – jo- Jan 08 '15 at 09:32
1

The Swing GUI is single threaded. That single thread being the EDT. If you wanted to introduce a second EDT (and still have the GUI working) you would have to also rewrite a lot of the internal Swing code to account for the added complexity of thread safety.

Adding another EDT would introduce more complexity for an unknown amount of increase (or decrease) in performance.

Kayaman
  • 72,141
  • 5
  • 83
  • 121
0

The following applies in case one uses the Sun toolkit. I am not sure about other Java implementations (have not tested it).

One can have multiple EDT threads provided the toolkit is initialized from multiple threads from separate thread groups.

PoC code:

import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.JTextArea;
import javax.swing.SwingUtilities;

public class Test {
    public static void main(String[] argv) {
        for (int i = 0; i < 5; i++) {
            // the separate thread group is needed at least for the Sun toolkit
            ThreadGroup tg = new ThreadGroup("Test Group " + i);
            Thread t = new Thread(tg, new Runnable() {
                @Override
                public void run() {
                    startApp();
                }
            }, "Test " + i);
            t.start();
        }
    }
    
    private static void startApp() {
        sun.awt.SunToolkit.createNewAppContext();
        final JFrame frm = new JFrame(Thread.currentThread().getName()) {
            @Override
            public void setVisible(boolean b) {
                super.setVisible(b);
                System.out.println("Closed");
                if (!b) dispose();
            }
        };
        final JTextArea ta = new JTextArea();
        frm.add(ta);
        JButton btn = new JButton("Dialog");
        // Showing a modal dialog will block only this frame
        btn.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                JOptionPane.showMessageDialog(frm, "Test message from " + frm.getTitle());
            }
        });
        frm.add(btn, BorderLayout.SOUTH);
        frm.setPreferredSize(new Dimension(300, 300));
        frm.pack();
        frm.show();
        Thread t = new Thread(new Runnable() {
            int i = 0;
            @Override
            public void run() {
                try {
                    while (true) {
                        i++;
                        if (!frm.isVisible()) break;
                        SwingUtilities.invokeAndWait(new Runnable() {
                            @Override
                            public void run() {
                                try {
                                    ta.getDocument().insertString(0, "Test " + i + " " +
                                        Thread.currentThread().hashCode()  + // This is to show that we are actually on a different thread as invokeAndWait is static and one may suspect a different behaviour
                                        "\n", null);
                                } catch (Throwable t) {
                                    t.printStackTrace();
                                }
                            }
                        });
                        Thread.sleep(1000 + (int)(Math.random() * 500));
                    }
                    Thread.sleep(4000); // This is just to show that the deamon thread might not have ended before the toolkit calls System.exit
                } catch (Throwable t) {
                    t.printStackTrace();
                }
                System.out.println("Thread " + Thread.currentThread().getName() + " exit");
            }
        });
        t.setDaemon(true);
        t.start();
    }
}

The above code demonstrates opening 5 frames on separate EDTs. When each frame is closed and disposed the respective EDT ends. When all the frames are closed and disposed the app will exit (automatically).

Notes:

  • one should not use (mix) components created in one EDT from another EDT;
  • closing the modal dialog from the example has the side effect of not returning the focus to the parent frame (as one would expect) but to any of the others.

I use the above approach in order to measure the height of a wrap enabled JTextArea that is populated with a multitude of different texts that might take some time and during that time I do not want to block the main UI. The result (the measured heights) is used on the main UI.

Tested with Java 1.6.0_25 and 1.8.0_60.