Which Swing Component Methods Are Thread Safe

Which Swing component methods are thread safe?

Google taught me that at least those are threadsafe. Here's an overview for the case that the link get broken again:


  • JTextPane
    • replaceSelection()
    • insertComponent()
    • insertIcon()
    • setLogicalStyle()
    • setCharacterAttributes()
    • setParagraphAttributes()

  • JTextArea
    • insert()
    • append()
    • replaceRange()

  • JTextComponent
    • replaceSelection()
    • setText()
    • print()
    • getPrintable()

  • UndoManager
    • All methods.

  • DefaultStyledDocument
    • insert()
    • setLogicalStyle()
    • setCharacterAttributes()
    • setParagraphAttributes()

  • StyleContext
    • addAttribute()
    • addAttributes()
    • removeAttribute()
    • removeAttributes()
    • reclaim()

  • AbstractDocument
    • render()
    • remove()
    • insertString()
    • createPosition()

  • PlainDocument
    • insertString()

  • HTMLDocument
    • setParagraphAttributes()

Is the Swing repaint() method still safe to use outside the EDT in Java 7+?

This is the official reference:

Swing's Threading Policy

In general Swing is not thread safe. All Swing components and related classes, unless otherwise documented, must be accessed on the event dispatching thread.

And the repaint method does not "document otherwise".

To doubly reassure you that you do not need to look any further than an individual method's Javadoc for the definitive answer, see for example how a method's thread safety was documented in Java 6 Javadoc.

Update

Apparently, more clarification is needed as to the distinction between normative specification, descriptive technical articles, and details of any specific implementation. What the Javadoc states is this: there is no guarantee that repaint is a thread-safe method. Incidentally, the often-discussed decision in Java 7 to remove the "thread-safe" designation from most of the Swing API was just that: a change in contract, not implementation.

The specific implementation of repaint in OpenJDK 7 appears to be thread-safe, a fact which has nothing to do with guarantees given by the specification. Code which relies on the thread safety of repaint or other methods is broken and is not guaranteed to behave properly on all Java implementations.

Swing Thread Safe Programming

Nothing theoretical about it. It's very practical. The SwingUtilities.invokeLater() method guarantees that the code within the Runnable will run on the Event Dispatch Thread (EDT). This is important because Swing is not thread-safe, thus anything related to the GUI (Swing, etc.) needs to run on the EDT. The EDT is a "it happens whenever it happens" thread that makes no guarantees about the order in which things are executed. If the GUI code is executed within a background thread (say, within a SwingWorker instance), then it can throw errors. I learned this the hard way: in my learning years, executing GUI-changing code within a background thread caused random, inconsistent RuntimeExceptions that I couldn't figure out. It was a good learning experience (SwingWorker has a doInBackground() method for background tasks and a done() method for EDT tasks).

In the same way you don't want to execute GUI code on a background thread, you also don't want to execute large operations (database queries, etc) on the EDT. This is because the EDT is dispatching all of the GUI events so everything on the EDT should be very short and sweet. You can easily see this with a JProgressBar set to indeterminate.

This SSCCE should illustrate it nicely. Notice the motion of the JProgressBar as method() is called, once on a background thread and once on a EDT thread.

import javax.swing.JFrame;
import javax.swing.JProgressBar;
import javax.swing.SwingWorker;

/**
*
* @author Ryan
*/
public class Test {

public static void main(String args[]) {
JFrame frame = new JFrame();
JProgressBar jpb = new JProgressBar();
jpb.setIndeterminate(true);
frame.add(jpb);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
new Task().execute();
}

public static void method() { // This is a method that does a time-consuming task.
for(int i = 1; i <= 5; i++) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(i);
}
}

static class Task extends SwingWorker<Void, Void> {

@Override
protected Void doInBackground() throws Exception {
/* Executing method on background thread.
* The loading bar should keep moving because, although method() is time consuming, we are on a background thread.
*/
method();
return null;
}

@Override
protected void done() {
/* Executing method on Event Dispatch Thread.
* The loading bar should stop because method() is time consuming and everything on the Event Dispatch Thread
* (like the motion of the progress bar) is waiting for it to finish.
*/

//
method();
}
}
}

Hope this helps.

Java: Swing Libraries & Thread Safety

  1. Never do long running tasks in response to a button, event, etc as these are on the event thread. If you block the event thread, the ENTIRE GUI will be completely unresponsive resulting in REALLY pissed off users. This is why Swing seems slow and crusty.

  2. Use Threads, Executors, and SwingWorker to run tasks NOT ON THE EDT ( event dispatch thread).

  3. Do not update or create widgets outside of the EDT. Just about the only call you can do outside of the EDT is Component.repaint(). Use SwingUtilitis.invokeLater to ensure certain code executes on the EDT.

  4. Use EDT Debug Techniques and a smart look and feel (like Substance, which checks for EDT violation)

If you follow these rules, Swing can make some very attractive and RESPONSIVE GUIs

An example of some REALLY awesome Swing UI work: Palantir Technologies. Note: I DO NOT work for them, just an example of awesome swing. Shame no public demo... Their blog is good too, sparse, but good

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

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

Quote from the javadoc:

Calls to an application's main method, or methods in Applet, are not invoked on the event dispatching thread. As such, care must be taken to transfer control to the event dispatching thread when constructing and showing an application or applet.

(emphasis mine)

If Swing models' getters aren't thread-safe, how do you handle them?

  1. No, not wrong, but as with any cross-thread communication you need to ensure you provide appropriate thread-safety mechanisms if you decide to do this (e.g. use of synchronized or volatile). For example I typically write my own TableModel implementations, typically sitting on List<X> where X is my business object. If I intend for other threads to query the List I will make this a synchronized Collection. It's also worth noting I would never normally update the List from other threads; only query it.
  2. Because it's exactly the same situation as with any other multi-threaded application.
  3. I typically make the collection synchronized (see 1).

Caveat

Despite my answer above I typically do not access models directly outside of the EDT. As Carl mentions, if the action of performing an update is invoked via some GUI action there's no need to perform any synchronization as the code is already being run by the EDT. However, if I wish to perform some background processing that will lead to the model being changed I will typically invoke a SwingWorker and then assign the results of doInBackground() from within the done() method (i.e. on the EDT). This is cleaner IMHO as the doInBackground() method has no side-effects.

Is JCheckBox.isSelected() thread safe?

No, it's not thread safe. Swing generally is not thread safe. You need to use invokeLater. On the other hand, you can make the current thread wait until the task ininvokeLater finishes:

private boolean isSelected(final AbstractButton button) {
if (SwingUtilities.isEventDispatchThread()) {
// a shortcut so the AWT thread won't wait for itself
return button.isSelected();
}
final AtomicBoolean isSelected = new AtomicBoolean();
final CountDownLatch latch = new CountDownLatch(1);
SwingUtilities.invokeLater(() -> {
try {
isSelected.set(button.isSelected());
} finally {
latch.countDown();
}
});
try {
latch.await();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
return isSelected.get();
}


Related Topics



Leave a reply



Submit