Could someone please explain "Note: This method should be called under AWT tree lock."?
Asked Answered
H

1

14

I am trying to make my program read the answers entered into a questionnaire-like form. For this purpose I plan to use getComponents() to get the answer fields I require (e.g. text fields, radio buttons, etc.), and then use methods like getText() to read the answers.

I have never used getComponents() and am just learning Java/Swing/AWT. The above warning in the documentation for getComponents() intimidates me, because I have no idea what a "tree lock" is, or where to find out what it is. Google has yielded nothing.

Even if getComponents() turns out to be an inappropriate solution to my problem, for the sake of learning I would still like my question answered.

Thanks! :)

Hayott answered 25/11, 2015 at 22:15 Comment(3)
Don't use 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.Televisor
The "tree lock" is meant to provide a safe guard for modifications to the UI itself (adding/removing components). If you're running the code correctly in the Event Dispatching Thread, you shouldn't need it - Also, what Hovercraft saidLoculus
@HovercraftFullOfEels, I see no problem with using getComponents(). 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
E
28

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

Enlistment answered 11/1, 2016 at 15:34 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.