3

I'm investigating a deadlock problem on setText but I need to first learn and understand about deadlocks. To this end I have created a short program to try and replicate what may be happening on the larger scale but I am unsure as to why my smaller program never deadlocks.

Here is my learning program:

public static void main(String[] a)
{
    JFrame frame = new JFrame();
    final JTextField p = new JTextField("start");

    JButton btn = new JButton("button");
    btn.addActionListener(new ActionListener()
    {
        @Override
        public void actionPerformed(ActionEvent e)
        {
            SwingUtilities.invokeLater(new Runnable(){
                @Override
                public void run(){
                    p.setText(String.valueOf(System.nanoTime()));
                }
            });
        }
    });

    frame.getContentPane().setLayout(new FlowLayout());
    frame.getContentPane().add(p);
    frame.getContentPane().add(btn);
    frame.setSize(400, 400);
    frame.setDefaultCloseOperation(frame.EXIT_ON_CLOSE);
    frame.setVisible(true);
}

I thought that modifications to swing could not be done in a separate thread, so I have a setText to change the JTextField on a button click in a invokeLater. Doing so should break the single thread rule, would this not cause a deadlock?

Aequitas
  • 2,205
  • 1
  • 25
  • 51

3 Answers3

2

Making changes to Swing components from other threads won't deadlock your program (at least not usually)--it's just that the JVM isn't obligated to reflect changes to state made in one thread in other threads unless there's a happens-before relationship, such as a synchronized block or access to a volatile field. The JVM might decide to read a variable's value once and never reread it in the current thread, which would mean that your update would never get seen by the thread that's drawing the UI, or it might get around to updating it at some unpredictable later time.

Using invokeLater to insert the update into the EDT ensures that there's a happens-before between that setText and the next draw operation.

Update: As you've now succeeded in making the code deadlock by moving where you queue the Runnable, the problem is that the EDT isn't running yet when you try to queue the operation on it.

chrylis -cautiouslyoptimistic-
  • 75,269
  • 21
  • 115
  • 152
  • why in [this](http://stackoverflow.com/questions/8865800/deadlock-when-using-settext-on-jtextarea-in-swing) question they get a deadlock in a similar setup to mine? What's the difference between them – Aequitas Jan 21 '16 at 03:45
  • @Aequitas For starters, you're not synchronizing on the Swing object (the frame), and then you're submitting your task after the Swing event thread has started (the other one tries to submit it before any of Swing has started up). – chrylis -cautiouslyoptimistic- Jan 21 '16 at 03:52
  • I see thank you, I moved the invokeLater into the main instead of button press and it now deadlocks. What do you mean about the synchonizing? – Aequitas Jan 21 '16 at 03:56
  • This is so confusing, with my deadlocking code, if I remove the setLayout line it will no longer deadlock, why would the layout change anything? – Aequitas Jan 21 '16 at 04:04
2

In the above example you are using a single thread. Swing, as most of the GUI environments, operates using an event queue. This queue contains things that must be handles, such as a click event, a text box edit event. These are all executed on the so called GUI thread. Swing continuously repaints the scene and processes events in the queue. The events are only processed on one thread, this is why the application freezes when you do long computation (or networking) in a click handler. When you call SwingUtilities.invokeLater, your code gets submitted and placed in the event queue. When Swing has some time, it executes it on the GUI thread.

For deadlock you need the following conditions:

  • at least two threads
  • that operate on resources A and B by first locking them
  • locking happens in different order on different threads

Example for potential deadlock:

Thread1:    Thread2:
lock(A)     lock(B)
lock(B)     lock(A)    <---- may deadlock here
do stuff    do stuff
free(B)     free(A)
free(A)     free(B)

The main difference in your example and the example you linked in your comment is that here you are creating your GUI on the main thread (similarly to the other example), but you're not invoking Swing's GUI thread until the user clicks on the button. The GUI is built on the main thread and does not interfere with the Swing thread. In the other example the GUI is built in parallel from two threads.

David Frank
  • 5,918
  • 8
  • 28
  • 43
  • See [this](http://stackoverflow.com/questions/8865800/deadlock-when-using-settext-on-jtextarea-in-swing) question, they have a similar setup to me using the invokeLater but still manage to get a deadlock. Why is that so on a single thread as you say? – Aequitas Jan 21 '16 at 03:44
  • More formally said, the conditions that *must* hold for deadlock are: mutual exclusion, hold-and-wait, no preemption, and circular wait. – ChiefTwoPencils Jan 21 '16 at 19:05
0

It is indeed not wise to invoke Swing methods from different threads, but not because of the risk of deadlocks. The main risks are:

  • Thread interference
  • Memory consistency errors

according to The Event Dispatch Thread. Deadlocks originate mainly from improperly ordered locking mechanisms in multiple threads. As many Swing objects apparently have no proper locking, deadlocks are not the main issue.

Gyzuh
  • 171
  • 1
  • 1
  • 5