A better solution would be something like this:
public class Whatever { private String text; private final Object TEXT_LOCK = new Object(); public void setText(final String newText) { synchronized (TEXT_LOCK) { text = newText; } SwingUtilities.invokeLater(new Runnable() { public void run() { someLabel.setText(newText); } }); } public String getText() { synchronized (TEXT_LOCK) { return text; } } }
This ensures that if two threads try to call setText at the same time, they will not be compressed with each other. The first thread will be set to text and set the UI value with this value. The second thread will also set the value to text and start the second update of the user interface.
The end result is that the user interface will ultimately display the most recent text value, but the internal text variable will immediately contain the most recent value.
A few notes:
- Using a separate locking object (i.e.,
TEXT_LOCK ) means that you are not vulnerable to code elsewhere that TEXT_LOCK monitor on the Whatever instance and inadvertently causes a deadlock. It's best to always keep tight control over your lock objects. It is also best to minimize the size of your synchronized blocks. - You can make the whole
setText method synchronized, with the caveat that it makes you potentially vulnerable to a dead end, as mentioned above. - Reading the value of
text also needs to be synchronized, although the Strings immutable. There are subtleties in the Java memory model, which means that you always need to synchronize around variables that can be read / written by multiple threads.
Check out Brian Goetz Java Concurrency in practice for a great dive into the complex parts of Concurrency (including the weirdness of the memory model).
source share