...
JCIP provides three class-level annotations to describe the programmer's design intent with respect to thread-safety. In this section we describe these annotations and provide examples of their use (much of the material below is also in the Javadoc for the annotations.)
The @ThreadSafe annotation is applied to a class to indicate that the class @ThreadSafe The class to which this annotation is applied is thread-safe. This means that no sequences of accesses (reads and writes to public fields, calls to public methods) may put the object into an invalid state, regardless of the interleaving of those actions by the runtime, and without requiring any additional synchronization or coordination on the part of the caller.
Example: The For example, the Aircraft
class shown below declares that it is thread-safe as part of its lock policy documentation. This class protects the x
and y
fields using a ReentrantLock
. The @Region and @RegionLock annotations (described below) document the precise locking policy that the promise of thread-safety is based upon.
Code Block | ||
---|---|---|
| ||
@ThreadSafe @Region("private AircraftState") @RegionLock("StateLock is stateLock protects AircraftState") public class Aircraft { private final Lock stateLock = new ReentrantLock(); ... @InRegion("AircraftState") private long x, y; ... public void setPosition(long x, long y) { stateLock.lock(); try { this.x = x; this.y = y; } finally { stateLock.unlock(); } } ... } |
The @Region and @RegionLock annotations (described below) document the precise locking policy that the promise of thread-safety is predicated upon.
Even if one or more @RegionLock or @GuardedBy annotations has been made to document the locking policy of a class this annotation can help to clarify that the overall class is thread-safe.
@Immutable The class to which this annotation is applied is immutable. This means that its state cannot be seen to change by callers, which implies that (1) all public fields are final, (2) all public final reference fields refer to other immutable objects, and (3) constructors and methods do not publish references to any internal state which is potentially mutable by the implementation. Immutable objects may still have internal mutable state for purposes of performance optimization; some state variables may be lazily computed, so long as provided they are computed from immutable state and that callers cannot tell the difference. Immutable objects are inherently thread-safe; they may be passed between threads or published without synchronization.
Example: The For example, the immutable Point
class below is considered thread-safe.
...
@NotThreadSafe The class to which this annotation is applied is not thread-safe. This annotation primarily exists for clarifying the non-thread-safety of a class that might otherwise be assumed to be thread-safe, despite the fact that it is a bad idea to assume a class is thread-safe without good reason.
Example: Most For example, most of the collection implementations provided in java.util
are not thread-safe. This could be documented for java.util.ArrayList
, for example, as shown below.
Code Block | ||
---|---|---|
| ||
package java.util; @NotThreadSafe public class ArrayList extends ... { ... } |
Documenting
...
Lock Policies
Wiki Markup |
---|
On page 28 of _Java Concurrency in Practice_ \[[Goetz 06|AA. Java References#Goetz 06]\], Goetz states: |
For each mutable state variable that may be accessed by more than one thread, all accesses to that variable must be preformed with the same lock head. In this case, we say that the variable is guarded by that lock.
Therefore, it is important to document which lock is used to protect each shared, mutable variable. JCIP provides the @GuardedBy annotation for this purpose while SureLogic provides the @RegionLock annotation. The use of both annotations is discussed in this section.
@GuardedBy The field or method to which this annotation is applied can only be accessed when holding a particular lock, which may be a built-in (synchronization) lock, or may be an explicit java.util.concurrent.Lock
.
Example: The below For example, the following MovablePoint
class implements a movable point with a capability to remember (i.e.that is, memo) its past locations.
Code Block | ||
---|---|---|
| ||
@ThreadSafe public class MovablePoint { @GuardedBy("this") double xPos = 1.0; @GuardedBy("this") double yPos = 1.0; @GuardedBy("itself") static final List memo = new ArrayList(); public void move(double slope, double distance) { synchronized (this) { xPos = xPos + ((1 / slope) * distance); yPos = yPos + (slope * distance); } } public static void memo(ex1 value) { synchronized (memo) { memo.add(value); } } } |
...
@RegionLock Declares a new region lock for the class to which this annotation is applied. This declaration creates a new named lock that associates a particular lock object with a region of the class. The region may only be accessed when the lock is held.
Examples: A For example, the SimpleLock
locking policy , named SimpleLock
, that indicates that synchronizing on the instance protects the all of the instance's state.
...
A locking policy, named StateLock
, that indicates that locking on the java.util.concurrent.Lock
stateLock
protects the named region, AircraftPosition
, that includes all of the mutable state used represent the position of the aircraft.
Code Block | ||
---|---|---|
| ||
@Region("private AircraftPosition") @RegionLock("StateLock is stateLock protects AircraftPosition") public class Aircraft { private final Lock stateLock = new ReentrantLock(); @InRegion("AircraftPosition") private long x, y; @InRegion("AircraftPosition") private long altitude; ... public void setPosition(long x, long y) { stateLock.lock(); try { this.x = x; this.y = y; } finally { stateLock.unlock(); } } ... } |
Construction of
...
hared, mutable objects
Typically, object construction is considered an exception to the locking policy. Why? This is because objects are thread-confined when they are constructed. They are confined to the thread that invoked the new
expression to construct the object. Subsequently, the object is safely published to other threads. At issue, is that the object does not become shared until the thread that invoked the new
expression expects it to. One approach is discussed in CON14-J. Do not let the "this" reference escape during object construction and can be expressed with the @Unique("return") annotation.
...
Sutherland and Scherlis in Composable Thread Coloring propose annotations that can document thread-confinement policies. Their approach is designed to allow verification of the annotations with the as written code.
Example: ExpressionFor example, expression, using the annotations proposed by Sutherland and Scherlis, of the design intent that a program has at most one AWT event dispatch thread, as many Compute threads as it wishes, and that the Compute thread is forbidden to handle AWT data structures or events.
Code Block | ||
---|---|---|
| ||
@ColorDeclare AWT, Compute @IncompatibleColors AWT, Compute @MaxColorCount AWT 1 |
...