Atomic Access and Assignment

- 221 -

10.6 Atomic Access and Assignment

Variables shared between multiple threads e.g., instance variables of objects have atomic assignment guaranteed by the Java language specification for all data types except for long s and double s. Actually, the storing of a value into a variable takes two primitive operations, a store and a write. However, the language specification also states that once a store operation occurs on a particular variable, no other store operation is allowed on that variable until the write operation has occurred. The specification allows long s and double s to be stored in two separate sets of store+write operations, hence their exception to atomicity. A similar atomic specification applies for reading variables. This means that access and update of variables are automatically synchronized as long as they are not long s or double s. If a method consists solely of a variable access or assignment, there is no need to make it synchronized for thread safety, and every reason not to do so for performance. Thread safety extends further to any set of statements that are accessing or assigning to a variable independently of any other variable values. The exclusion here precludes setting a variable that depends on the value of another variable as being thread-safe; this would be two separate operations, which is inherently not thread-safe. For example: public void setMeObject o {me = o;} public Object getMe {return me;} are thread-safe methods, with no need for synchronized modifiers to be added to the method declaration. On the other hand: public void setMeObject o {ifoverwrite me = o;} is not thread-safe: overwrite may be true at the time of checking in the if statement, but false by the time of the subsequent assignment statement. Anything more complex than simple assignments and accesses is probably not thread-safe: it depends on whether any particular intermediate state that can be accessed is considered corrupt by the application. Consider the code being halted before or after any particular atomic statement, and decide whether or not another thread could now access a corrupt application state. Combining several calls to methods that atomically assign variables is the same problem as combining several calls to synchronized methods. The individual calls are executed atomically, but the combination is not necessarily atomic: public void setMe1Object o {me = o;} public void setMe2Object o {me = o;} public void setBothObject o1, Object o2 {setMe1o1;setMe2o2;} For these three methods, it does not matter whether setMe1 and setMe2 are synchronized or not. setBoth is not synchronized, so it can be interrupted between the calls to setMe1 and setMe2 , allowing another thread to update one of the instance variables. This can leave the object in a potentially corrupt application state if both instance variables are always supposed to be updated together. Specifically, if two threads call the setBoth method simultaneously, the outcome is not predictable unless setBoth is synchronized. Synchronization Ordering It is easy to confuse exactly what synchronization does. Synchronization ensures that a set - 222 - of statements executes exclusively for a particular monitor. Synchronization does not guarantee the order of execution of synchronized blocks. If two threads try to execute a synchronized block simultaneously, one succeeds first, but there is no guarantee as to which one that is. Atomic assignment is the case where the set of synchronized statements is one statement, and the synchronization is set by the VM. When considering atomic assignment, you might ask the question, What if a context switch occurs during the method call setup or tear down? When does the synchronization happen, and what happens with the context switch? The actual moment when the synchronization occurs does not matter. It does not matter if a context switch happens at any time before or after a set of synchronized statements. Either the synchronized set has not been entered, or it has been completed. Only the actual granting of the lock matters, and that is atomic with respect to all interested threads. Until you reach an atomic assignment statement, it makes no difference whether another atomic assignment on the same variable occurs. This is purely the ordering of assignments, which is not guaranteed with synchronization anyway. After the atomic assignment is finished, it is complete. A context switch hitting the method tear down does not matter. The only reason to synchronize a simple updator is to avoid a corrupt assignment, i.e., two threads simultaneously updating the same variable, and the resulting value being neither of the updated values. This can indeed occur for double s and long s, but not for other data types. For serious number crunching involving double s and long s, I recommend using separate data structures for each thread or using a VM that guarantees atomic assignment for double s and long s. A longer discussion about Javas atomicity can be found in an article by Art Jolin, [6] where he discusses unsynchronized thread-safe data structures, including why a binary tree specifically the AWTEventMulticaster class can be thread-safe without any synchronized methods. [6] Javas Atomic Assignment, Java Report, August 1998.

10.7 Thread Pools