How to initialize gui objects in a thread-safe way in java swing?

I am reading Thinking in Java and the author points out that the main method should not call swing methods. As an example of this practice, he presents the following piece of code (available on his web page):

//: gui/SubmitSwingProgram.java
import javax.swing.*;
import java.util.concurrent.*;

public class SubmitSwingProgram extends JFrame {
  JLabel label;
  public SubmitSwingProgram() {
    super("Hello Swing");
    label = new JLabel("A Label");
    add(label);
    setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    setSize(300, 100);
    setVisible(true);
  }
  static SubmitSwingProgram ssp;
  public static void main(String[] args) throws Exception {
    SwingUtilities.invokeLater(new Runnable() {
      public void run() { ssp = new SubmitSwingProgram(); }
    });
    TimeUnit.SECONDS.sleep(1);
    SwingUtilities.invokeLater(new Runnable() {
      public void run() {
        ssp.label.setText("Hey! This is Different!");
      }
    });
  }
} ///:~

      

Then the gui object is created and initialized using the invokeLater method, making it thread safe. But a few pages later, the author presents the following code:

//: gui/Button2.java
// Responding to button presses.
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import static net.mindview.util.SwingConsole.*;

public class Button2 extends JFrame {
  private JButton
    b1 = new JButton("Button 1"),
    b2 = new JButton("Button 2");
  private JTextField txt = new JTextField(10);
  class ButtonListener implements ActionListener {
    public void actionPerformed(ActionEvent e) {
      String name = ((JButton)e.getSource()).getText();
      txt.setText(name);
    }
  }
  private ButtonListener bl = new ButtonListener();
  public Button2() {
    b1.addActionListener(bl);
    b2.addActionListener(bl);
    setLayout(new FlowLayout());
    add(b1);
    add(b2);
    add(txt);
  }
  public static void main(String[] args) {
    run(new Button2(), 200, 150);
  }
} ///:~

      

where SwingConsole:

//: net/mindview/util/SwingConsole.java
// Tool for running Swing demos from the
// console, both applets and JFrames.
package net.mindview.util;
import javax.swing.*;

public class SwingConsole {
  public static void
  run(final JFrame f, final int width, final int height) {
    SwingUtilities.invokeLater(new Runnable() {
      public void run() {
        f.setTitle(f.getClass().getSimpleName());
        f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        f.setSize(width, height);
        f.setVisible(true);
      }
    });
  }
} ///:~

      

So, contrary to the previous example, an object that implements a JFrame is created and initialized in the main method / main thread.

My question is: (1) Is the second example wrong, or is the first exaggerated? (2) Is it enough that I only call swing methods via invokeLater after calling setVisible and before that statement can the swing methods be called safely on the main thread?

+1


source to share


2 answers


The second example is wrong. Swing components must be created and used from the event flow. See https://docs.oracle.com/javase/tutorial/uiswing/concurrency/initial.html .

Quoting from the javadoc :



Calling the main method of the application or methods in the Applet is not called on the event dispatch thread. Therefore, care should be taken to transfer control to the event dispatch thread when constructing and displaying an application or applet.

(emphasis mine)

+5


source


The main () example might be a little flawed. The rule AFAIK is that only the main thread is allowed to execute Swing updates / redraws, etc., and the main () method to define is the main thread. All other threads should schedule stuff via SwingUtilities.invokeLater (...). Perhaps even for the main thread, this improves latencies as you can take a long time on the main thread and the GUI is updated by the enemy whenever the main thread is less busy. EDIT: tested wrong



-1


source







All Articles