Frage

So basically I will be updating the GUI for my application, so I use the static method

void javax.swing.SwingUtilities.invokeLater(Runnable doRun)

But I wish to put my initialize code in the runnable and the logical code outside. But if I invoke the invokeLater method, my main-thread and the AWT thread will be in a race condition, because my main-thread will try invoke my logical code just after the invokeLater method has been called, but since the JFrame (in my case) is not initialized yet, so my main-thread will most likely win the race and therefore throw an exception.

I thought I might be able to wait for the runnable to be executed by the Event-Dispatching thread and then notify from the runnable when it has initialized the initialize code. But for some reason, the code below (abbreviated) doesn't work.

For a lightweight application, the runnable is likely to win the race condition, hence not producing any exception, but in my application the initialize code is longer.

SSCCE:

public class SSCCE {
    private static JFrame frame;
    public static void main(String[] args) {
        Runnable r = new Runnable() {
            public void run() {
                synchronized (this) {
                    frame = new JFrame();
                    frame.setTitle("JFrame");
                    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                    frame.setLocationRelativeTo(null);
                    this.notifyAll();
                    System.out.println("Notified all");
                }
            }
        };
        synchronized (r) {
            try {
                System.out.println("Waiting"); // EDIT this line was after the wait method, and therefore it didn't execute.
                r.wait();
            } catch (InterruptedException e) {
                throw new RuntimeException("Unexpected disruption whilst waiting for the \"Event-Dispatching thread\".",e);
            }
        }
        SwingUtilities.invokeLater(r);
        frame.setVisible(true);
        frame.getContentPane().add(new JLabel("Hello World!"));
        frame.pack();
    }
}

Thanks in advance!

War es hilfreich?

Lösung

Your mistake is quite simple: you are calling (well, you were if it worked) invokeLater after you are trying to wait for the result:

synchronized (r) {
  try {
    …
    r.wait(); // <- here you are waiting
  } catch (InterruptedException e) {
    …
  }
}
SwingUtilities.invokeLater(r); // <- never reached, you are waiting above

So the obvious fix would be to call SwingUtilities.invokeLater(r); first and wait afterwards.


But that is rather obsolete, there is the method SwingUtilities.invokeAndWait(r); which does the job of scheduling a Runnable in the EDT and waiting for its completion. So you don’t need to program your synchronization and notification by yourself.


But note that your lines in your main method following your invokeLater are manipulating the UI and hence should be placed within the EDT just like the construction of the JFrame as otherwise you are still violating the Swing’s threading policy and still may get race conditions.

Your main thread may execute things concurrently to the EDT and it may wait for the UI construction if you wish. But it should not manipulate the UI itself. Such manipulation would also contradict your statement that you want to have the “logical code outside” as manipulating a JFrame does not belong to “logical code”.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top