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 setMe(Object 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 setMe(Object o) {if(overwrite) 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 setMe1(Object o) {me = o;} public void setMe2(Object o) {me = o;} public void setBoth(Object o1, Object o2) {setMe1(o1);setMe2(o2);}
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.
A longer discussion about Java’s atomicity can be found in an
article by Art Jolin,[70] 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.
18.117.196.217