I was also curious about the inner workings of the AWT tree lock
(AWTTreeLock
/TreeLock
) so I did some research which I will try to summarize here.
First of all bear in mind, that in Swing one should only access Components
on the AWT Event Dispatch Thread (EDT) (see Swing's Threading Policy). Therefore it is generally recommended to always prefere SwingUtilities#invokeLater() instead of Component#getTreeLock()
for GUI synchronization tasks. If one can guarantee, that all access to the GUI components is done on the EDT, there should usually be no need to explicitly synchonize on Component#getTreeLock()
.
According to David Holmes AWT was on the other hand originally intended to be accessible by multiple threads, which might also explain why the Javadoc of strict AWT classes (e.g. Component
and Container
) do not inclcude the thread-safety warning usually found in the Javadoc of Swing classes ("Swing is not thread safe..."). In this regard Component#getTreeLock()
returns the static final
lock Object shared by all AWT components. AWT classes synchronize on this object when modifying/accessing the component tree (e.g. during Container#addImpl()
) and it is the client's responsibility to synchronize on this lock when multiple threads access the component tree. From this point of view Component#getTreeLock()
seems to be a relict from this original AWT design and today mainly exists for backward compatibility and arguably for some border case uses like e.g. accessing components from multiple threads before the AWT event dispatch thread has started. On the other hand additional synchronization on the tree lock doesn't hurt either in some cases (I guess the small performance penalty can easily be neglected) - e.g. I'd still recommend tree lock synchronization during calls to Container#getComponents()
, Container#getComponentCount()
and Container#getComponent(int)
(as documented in the Javadoc of these methods) and possibly also during layout operations in Java LayoutManagers (as suggested in the Javadoc of Component#getTreeLock()
- actually these two rules are more or less the same, since most of the time LayoutManagers
make use of exactly these three methods).
Available documentation and information about the AWT tree lock
It's a shame that a public Java API method intended for GUI thread synchronization is so poorly documented. Actually it seems the only "documentation" is provided by the Javadoc of Component#getTreeLock()
itself, which might very well be listed as a good example for bad practice getter documentation:
/**
* Gets this component's locking object (the object that owns the thread
* synchronization monitor) for AWT component-tree and layout
* operations.
* @return this component's locking object
*/
Other than that there are some hints about the intended usage of the tree lock in the documentation of Container#getComponents()
, Container#getComponentCount()
and Container#getComponent(int)
.
Note: This method should be called under AWT tree lock.
and in the source code comments of some Java classes referencing Component#getTreeLock()
like e.g. java.swing.GroupLayout
:
public void invalidateLayout(Container parent) {
checkParent(parent);
// invalidateLayout is called from Container.invalidate, which
// does NOT grab the treelock. All other methods do. To make sure
// there aren't any possible threading problems we grab the tree lock
// here.
synchronized(parent.getTreeLock()) {
[...]
One interesting detail here is, that Java LayoutManagers
do most of the time synchronize on the tree lock when doing layout operations - e.g. in the methods layoutContainer()
, minimumLayoutSize()
and preferredLayoutSize()
of FlowLayout
, BorderLayout
and GridLayout
. But strangely enough this pattern is not consistently applied to all Java LayoutManagers
- e.g. GridBagLayout
does synchronize on the tree lock in the method getLayoutInfo()
but does not do so in the method arrangeGrid()
, although both methods access the component tree. BoxLayout
on the other hand does not synchronize on the tree lock at all (see also this thread). FWIW the Java tutorial for Creating a Custom Layout Manager does not mention tree locks at all and the given example does not do any synchronization. Still, a lot of third party LayoutManagers
also adhere to this synchronization pattern like e.g. JGoodies FormLayout and the Custom Layouts Blog says that
Synchronizing on the object returned by the container's getTreeLock
method ensures thread safety while performing the layout
In my opinion this inconsistency and the lack of proper documentation is very confusing. E.g. I still have no idea under which circumstances it would absolutely be necessary for LayoutManagers
to synchronize on the tree lock. I'd guess that normally (i.e. if component access stays on the EDT) synchronization should not be necessary. On the other hand this extra bit of synchronization certainly doesn't hurt either...
Also Java bug JDK-6784816 provides some information on the subject. It states that
AWT tree lock is a public lock that should be used for any hiearchy or
layout operations by developers, not by AWT.
and that
applications that call them [the methods Container#getComponents()
, Container#getComponentCount()
and Container#getComponent(int)
] without AWT tree lock must realize that
they do this on their own risk. For example, due to some timings
changes, the methods may return incorrect values without proper
synchronization under AWT tree lock.
I would still strongly advice against solely relying on tree lock synchronization instead of synchronization on the EDT, considering the inconsistent synchronization on the tree lock that can be found in the Java API alone (not to mention possible third party API's). I'll conclude with Thomas Hawtins words on this subject:
Swing is single threaded, AWT is not. However, AWT has thousands
(literally) of threading bugs, which are unlikely to be fixed. It is
unreasonable to expect multithreaded AWT application code not to be
completely broken.
Further references and links
getComponents()
for this as you'll be building a fragile GUI that will break if anything is structurally changed later. Instead use standard OOP practices, including getter methods to allow other classes to be able to extract the state of key components. – TelevisorgetComponents()
. It is a getter and therefore using standard OOP. The state of the application may be obscured or changed in any number of ways regardless of which getter or setter is used. OOP offers no help here. If you want a consistent view of the world you need to use pure functional programming (which means immutable data structures, too). See Clojure for a practical and reliable way to add mutability to such an application. – Uglify