14 Replies - 854 Views - Last Post: 05 August 2013 - 03:43 PM Rate Topic: -----

#1 cfoley  Icon User is online

  • Cabbage
  • member icon

Reputation: 1907
  • View blog
  • Posts: 3,953
  • Joined: 11-December 07

The wrong way to Swing

Posted 04 August 2013 - 04:39 AM

Right, so I know all code that interacts with a Swing gui should execute on the event dispatch thread, including the initial instantiation. This is how you should do it. Of course, the JFrame in this example is a placeholder for the actual gui in a real application.

import javax.swing.*;

public class TheRightWay {

	public static void main(String[] args) {
		SwingUtilities.invokeLater(new Runnable() {

			@Override
			public void run() {
				new JFrame().setVisible(true);

			}

		});
	}
}


But that's not what I do. I've been creating and setting my guis to visible on the main thread for the past thirteen years with no ill effects.

import javax.swing.*;

public class WhatIDo {

	public static void main(String[] args) {
		new JFrame().setVisible(true);
	}

}



What's the deal? Have I created a bunch of ticking time bombs just waiting to blow up on an unsuspecting user? Or is it, as I suspect, that if there is no gui already in existence, there is no chance of a memory corruption?

Is This A Good Question/Topic? 4
  • +

Replies To: The wrong way to Swing

#2 GregBrannon  Icon User is offline

  • D.I.C Lover
  • member icon

Reputation: 2194
  • View blog
  • Posts: 5,222
  • Joined: 10-September 10

Re: The wrong way to Swing

Posted 04 August 2013 - 04:44 AM

This Trail is a good explanation of the whys and possible pitfalls of doing things the "wrong" way. Run time errors caused by Swing(ing) the wrong way are usually hard to recognize and impossible to isolate to the true root cause, usually attributed to "sun spots" or other vague OS problems that may or may not really exist.
Was This Post Helpful? 2
  • +
  • -

#3 cfoley  Icon User is online

  • Cabbage
  • member icon

Reputation: 1907
  • View blog
  • Posts: 3,953
  • Joined: 11-December 07

Re: The wrong way to Swing

Posted 04 August 2013 - 04:53 AM

I've read that trail before which is why I know the right way to do it. The best explanation it gives to my specific question is this:

Quote

Some Swing component methods are labelled "thread safe" in the API specification; these can be safely invoked from any thread. All other Swing component methods must be invoked from the event dispatch thread. Programs that ignore this rule may function correctly most of the time, but are subject to unpredictable errors that are difficult to reproduce.


I suppose a better question for me to ask would have been: Has anyone ever had any problems doing it the wrong way?

Or: Can anyone write a program that behaves incorrectly by doing it the wrong way?
Was This Post Helpful? 1
  • +
  • -

#4 GregBrannon  Icon User is offline

  • D.I.C Lover
  • member icon

Reputation: 2194
  • View blog
  • Posts: 5,222
  • Joined: 10-September 10

Re: The wrong way to Swing

Posted 04 August 2013 - 06:02 AM

Have I, yes, at least I think so. My own example is quite complex and prone to fail when it feels like it. In other words, I don't know how to reliably reproduce the undesirable behavior, but it sucks when it happens.

Another Java architecture history lesson that leads to an example of how doing things the wrong way can be painful is found in this article, referenced by this article.

I hope the first link answers your second restated question. If not sufficiently, I'm out.
Was This Post Helpful? 4
  • +
  • -

#5 farrell2k  Icon User is offline

  • D.I.C Lover
  • member icon

Reputation: 797
  • View blog
  • Posts: 2,423
  • Joined: 29-July 11

Re: The wrong way to Swing

Posted 04 August 2013 - 06:53 AM

View Postcfoley, on 04 August 2013 - 11:39 AM, said:

Or is it, as I suspect, that if there is no gui already in existence, there is no chance of a memory corruption?


Exactly. The problem lies in swing's painting mechanism. If nothing is realized on the screen, there is no chance of any problems. If one component visible and another is realized on a thread other than the EDT, as they know absolutely nothing about one another, there is a slight possibility that AWT could be redrawing one on the EDT while the other is redrawing on say the main thread. Of course the two simply cannot repaint at the same time, so what happens? Do they deadlock and wait for one another, or does a race condition happen and something gets painted wrong?

You'll definitely see this in games where the person draws in a separate thread with a graphics object via getGraphics(), while not calling setIgnoreRepaint() on the other swing components. Play the game long enough, and an OS request for an AWT repaint will get in the way of a repaint by their game loop thread and the whole thing will lock up.

EDIT: You'll see a reference to this in the programming tips section in this article.

So create a JFrame with a jpanel with a swing timer that fires a repaint() every 1ms. Now in the JPanel create a Thread that grabs a graphics object with getGraphics(), draws something, then sleeps for 1 ms. Eventually, you'll se it happen.

This post has been edited by farrell2k: 04 August 2013 - 07:09 AM

Was This Post Helpful? 2
  • +
  • -

#6 CasiOo  Icon User is online

  • D.I.C Lover
  • member icon

Reputation: 1275
  • View blog
  • Posts: 2,839
  • Joined: 05-April 11

Re: The wrong way to Swing

Posted 04 August 2013 - 07:35 AM

Quote

If one component visible and another is realized on a thread other than the EDT, as they know absolutely nothing about one another, there is a slight possibility that AWT could be redrawing one on the EDT while the other is redrawing on say the main thread.

If doing active rendering, then it could happen yes
You shouldn't run into any problems when you are not doing active rendering. The swing components are all doing passive rendering (Of the components I know)
A call to repaint() will put a paint event in the queue, and it will all be done on the EDT. Even when setting a component visible from outside the EDT

You should still be doing it on the EDT though, there really is no excuse
Was This Post Helpful? 2
  • +
  • -

#7 farrell2k  Icon User is offline

  • D.I.C Lover
  • member icon

Reputation: 797
  • View blog
  • Posts: 2,423
  • Joined: 29-July 11

Re: The wrong way to Swing

Posted 04 August 2013 - 08:22 AM

View PostCasiOo, on 04 August 2013 - 02:35 PM, said:

A call to repaint() will put a paint event in the queue, and it will all be done on the EDT.


Yes.

View PostCasiOo, on 04 August 2013 - 02:35 PM, said:

Even when setting a component visible from outside the EDT


setVisible() is not called on the EDT.
Was This Post Helpful? 1
  • +
  • -

#8 CasiOo  Icon User is online

  • D.I.C Lover
  • member icon

Reputation: 1275
  • View blog
  • Posts: 2,839
  • Joined: 05-April 11

Re: The wrong way to Swing

Posted 04 August 2013 - 10:11 AM

View Postfarrell2k, on 04 August 2013 - 03:22 PM, said:

View PostCasiOo, on 04 August 2013 - 02:35 PM, said:

A call to repaint() will put a paint event in the queue, and it will all be done on the EDT.


Yes.

View PostCasiOo, on 04 August 2013 - 02:35 PM, said:

Even when setting a component visible from outside the EDT


setVisible() is not called on the EDT.


no, but setVisible will make a repaint happen which is on the EDT
Was This Post Helpful? 2
  • +
  • -

#9 farrell2k  Icon User is offline

  • D.I.C Lover
  • member icon

Reputation: 797
  • View blog
  • Posts: 2,423
  • Joined: 29-July 11

Re: The wrong way to Swing

Posted 04 August 2013 - 11:39 AM

I think you are right now.

public void show() {
        if (!visible) {
            synchronized (getTreeLock()) {
                visible = true;
                mixOnShowing();
                ComponentPeer peer = this.peer;
                if (peer != null) {
                    peer.setVisible(true);
                    createHierarchyEvents(HierarchyEvent.HIERARCHY_CHANGED,
                                          this, parent,
                                          HierarchyEvent.SHOWING_CHANGED,
                                          Toolkit.enabledOnToolkit(AWTEvent.HIERARCHY_EVENT_MASK));
                    if (peer instanceof LightweightPeer) {
                        repaint();
                    }
                    updateCursorImmediately();
                }

                if (componentListener != null ||
                    (eventMask & AWTEvent.COMPONENT_EVENT_MASK) != 0 ||
                    Toolkit.enabledOnToolkit(AWTEvent.COMPONENT_EVENT_MASK)) {
                    ComponentEvent e = new ComponentEvent(this,
                                                          ComponentEvent.COMPONENT_SHOWN);
                    Toolkit.getEventQueue().postEvent(e);
                }
            }
            Container parent = this.parent;
            if (parent != null) {
                parent.invalidate();
            }
        }
    }



So then what could cause lock up when another is realized on a thread other than the edt? I am confused now.

This post has been edited by farrell2k: 04 August 2013 - 11:56 AM

Was This Post Helpful? 1
  • +
  • -

#10 pbl  Icon User is offline

  • There is nothing you can't do with a JTable
  • member icon

Reputation: 8315
  • View blog
  • Posts: 31,836
  • Joined: 06-March 08

Re: The wrong way to Swing

Posted 04 August 2013 - 02:32 PM

The

           public void run() {
                new JFrame().setVisible(true);
            }


is just a insurance for users not really knowing how the standard threads work.
If your code is really well writen, you do not need to instantiate the JFrame in a different thread... it just saves a lot of trouble (and gives you a thread of margin) if the rest is not well writen.

This post has been edited by pbl: 04 August 2013 - 07:43 PM

Was This Post Helpful? 2
  • +
  • -

#11 jjh08  Icon User is offline

  • D.I.C Head

Reputation: 55
  • View blog
  • Posts: 190
  • Joined: 13-July 12

Re: The wrong way to Swing

Posted 04 August 2013 - 06:01 PM

View PostGregBrannon, on 04 August 2013 - 04:44 AM, said:

usually attributed to "sun spots" or other vague OS problems that may or may not really exist.

What is a "sun spot"? I've never heard of that.
Was This Post Helpful? 2
  • +
  • -

#12 cfoley  Icon User is online

  • Cabbage
  • member icon

Reputation: 1907
  • View blog
  • Posts: 3,953
  • Joined: 11-December 07

Re: The wrong way to Swing

Posted 05 August 2013 - 03:26 AM

I think he is referring to one of these:
http://en.wikipedia.org/wiki/Sunspot

Even though Sun Microsystems developed a Sun SPOT.

Thanks for the replies everyone. This is getting quite interesting.

So, two possibilities for threads screwing things up are thread interference and memory inconsistency. I don't see thread interference being a problem unless you are doing something really crazy.

Let's concentrate on memory inconsistency. Can we write a gui that has a high chance (so we can see it failing if we run it 10-20 times) of demonstrating memory inconsistency during the creation of the Swing Gui?
Was This Post Helpful? 1
  • +
  • -

#13 farrell2k  Icon User is offline

  • D.I.C Lover
  • member icon

Reputation: 797
  • View blog
  • Posts: 2,423
  • Joined: 29-July 11

Re: The wrong way to Swing

Posted 05 August 2013 - 06:06 AM

If you look at the implementation of show(), I think Thread interference could be a problem. It's possible that a repaint could be done before invalidate() if done from a thread other than the EDT, since it could be pre-empted at any time by the EDT. i.e. the LayoutManager doesn't have time to do its work on the new component.

Main thread executes this:
public void show() {
        if (!visible) {
            synchronized (getTreeLock()) {
                visible = true;
                mixOnShowing();
                ComponentPeer peer = this.peer;
                if (peer != null) {
                    peer.setVisible(true);
                    createHierarchyEvents(HierarchyEvent.HIERARCHY_CHANGED,
                                          this, parent,
                                          HierarchyEvent.SHOWING_CHANGED,
                                          Toolkit.enabledOnToolkit(AWTEvent.HIERARCHY_EVENT_MASK));
                    if (peer instanceof LightweightPeer) {
                        repaint();
                    }



The EDT gets the repaint() requests and takes over here, before the main Thread has finished with this below.


                    updateCursorImmediately();
                }

                if (componentListener != null ||
                    (eventMask & AWTEvent.COMPONENT_EVENT_MASK) != 0 ||
                    Toolkit.enabledOnToolkit(AWTEvent.COMPONENT_EVENT_MASK)) {
                    ComponentEvent e = new ComponentEvent(this,
                                                          ComponentEvent.COMPONENT_SHOWN);
                    Toolkit.getEventQueue().postEvent(e);
                }
            }
            Container parent = this.parent;
            if (parent != null) {
                parent.invalidate();
            }
        }
    }


Was This Post Helpful? 2
  • +
  • -

#14 CasiOo  Icon User is online

  • D.I.C Lover
  • member icon

Reputation: 1275
  • View blog
  • Posts: 2,839
  • Joined: 05-April 11

Re: The wrong way to Swing

Posted 05 August 2013 - 09:58 AM

Well the tree lock is interesting. Every layout operation will be done when inside the tree lock's monitor
There is therefore very high chance that the EDT will be the owner of the tree lock when painting the components, otherwise it would be a concurrency nightmare I believe

This post has been edited by CasiOo: 05 August 2013 - 09:58 AM

Was This Post Helpful? 3
  • +
  • -

#15 pbl  Icon User is offline

  • There is nothing you can't do with a JTable
  • member icon

Reputation: 8315
  • View blog
  • Posts: 31,836
  • Joined: 06-March 08

Re: The wrong way to Swing

Posted 05 August 2013 - 03:43 PM

Don't be over paranoid neither
The always quoted message "Swing is not thread safe" means that a display may be refresh with a delay of 1/60 second if you do not follow the rules.

In an application with animation that might cause screen flickering. This latency of 1/60 second may also be crucial for a cardio monitor system but can probably be ignore in 99% of the applications.

75% of Swing code found on the net have complety useless repaint() calls just showing that these developpers didn't really know what they were doing :)
Was This Post Helpful? 1
  • +
  • -

Page 1 of 1