1
votes

If I understand correctly then when I create GUI swing components, for example I have this:

public class frameExample extends JFrame{
    public frameExample(){
    //Here adding bunch if components
    setVisible(true);
    }
}

So as long as I don't call the setVisible method the components are being made from the thread the instance was created. So if in a class where i have my main method write:

JFrame test=new frameExample();

and I sysout

Thread.currentThread.getName(); 

in the constructor in frameExample just before setVisible I should get: main.

After that the responsibility of creating and maintaining swing elements are passed to the event-dispatch-thread and because it is not thread safe every component add/remove/modify should be done within the EDT thread.

So I should place my setVisible as the last line of code in my constructor, or call it separately.

As I understand all event listening goes through the EDT. So if I create a new component within for example an actionPerformed method it should do fine.

Also if I pass a runnable instance to invokeLater or invokeAndWait then all the run() method will be done by the EDT.

So here is why I'm confused.

I made this code:

public class GUI extends JFrame {


JButton btn = new JButton("Change");
JMenuBar m = new JMenuBar();

public GUI() {
    super("Test");
    setSize(400, 400);
    setDefaultCloseOperation(EXIT_ON_CLOSE);

    m.add(new JMenu("menu"));
    add(m, BorderLayout.NORTH);

    add(btn, BorderLayout.SOUTH);
    System.out.println("Current thread: before setVisible "+Thread.currentThread().getName());

    setVisible(true);

    System.out.println("Current thread: after setVisible "+Thread.currentThread().getName());
    btn.addActionListener(new ActionListener() {

        @Override
        public void actionPerformed(ActionEvent e) {

            add(new JButton("testbtn1"), BorderLayout.EAST);
            add(new JButton("testbtn2"));
            System.out.println("Current thread: "+Thread.currentThread().getName());


            new Thread(new Runnable() {

                @Override
                public void run() {
                    // TODO Auto-generated method stub
                    for (int i = 0; i < 1E8; i++) {
                        Math.sin(5.0);
                    }
                    System.out.println("Current thread: "+Thread.currentThread().getName());
                }
            }).start();

        }
    });
}

}

So in my anonim class where I add the two buttons im in the EDT, but the components are not added to my frame just after i resize it (which forces the edt to update its components??? what is the reason for this?).

So i dont get my new components even within edt, however when i create a random thread outside edt and make it change some gui element property (for example setText) in just works fine outside the edt.

So my first question is: why my components are not updated within edt and why they are visible after resize

second one: why can i make changes to swing components outside edt and everything works fine? Is this just random thread behvior where for example things just work fine without sync block but when you rerun you program at some point it will eventually crash because the lack of sync.

1

1 Answers

3
votes

So as long as I don't call the setVisible method the components are being made from the thread the instance was created

Wrong. As long as you do not specifically create a new Thread or use a utility method (like for example the SwingUtilities#invoke... methods), each call is invoked on the current Thread, including the setVisible call.

It looks like you think that making Swing components visible somehow makes your code switch threads. No, the painting of the Swing components will happen on the EDT. And as you correctly stated, Swing is not thread safe. That is why you should create the component on the EDT as well, and not on another thread. It might work without problems the majority of the time, but eventually you will stumble upon weird bugs.

As I understand all event listening goes through the EDT. So if I create a new component within for example an actionPerformed method it should do fine.

Also if I pass a runnable instance to invokeLater or invokeAndWait then all the run() method will be done by the EDT.

Correct.

So in my anonim class where I add the two buttons im in the EDT, but the components are not added to my frame just after i resize it

Adding components to a Container requires you to revalidate the layout (see the javadoc of the Container#add method). Just call

revalidate();
repaint();

after you add the buttons and it will work as expected. Manually resizing the frame has the same effect as you already noticed.

So i dont get my new components even within edt, however when i create a random thread outside edt and make it change some gui element property (for example setText) in just works fine outside the edt.

As stated before, it might work most of the time, but there is no guarantee it will work 100% of the time. The reason the adding of your components did not work as expected is explained above, and has nothing to do with threading issues.