...
JCIP provides three class-level annotations to describe the programmer's design intent with respect to thread-safety.
The @ThreadSafe
annotation is applied to a class to indicate that it is thread-safe. This means that no sequences of accesses (reads and writes to public fields, calls to public methods) can leave the object in an inconsistent state, regardless of the interleaving of these accesses by the runtime or any external synchronization or coordination on the part of the caller.
...
Code Block | ||
---|---|---|
| ||
@ThreadSafe @Region("private AircraftState") @RegionLock("StateLock is stateLock protects AircraftState") public final 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 document the locking policy upon which the promise of thread-safety is predicated.
Even when one or more @RegionLock
or @GuardedBy
annotations have been used to document the locking policy of a class, the @ThreadSafe
annotation provides an intuitive way for reviewers to learn that the class is thread-safe.
The @Immutable
annotation is applied to immutable classes. Immutable objects are inherently thread-safe; once they are fully constructed, they may be published via a reference and shared safely among multiple threads.
...
It is not necessary to document the immutability of
enum
types. Unless it is obvious from the return type, static factories must document the thread safety of the returned object, as demonstrated byCollections.synchronizedMap
.
The @NotThreadSafe
annotation is applied to classes that are not thread-safe. Many classes fail to document whether they are safe for multithreaded use. Consequently, a programmer has no easy way to determine whether the class is thread-safe. This annotation provides clear indication of the class's lack of thread-safety.
...
JCIP provides the @GuardedBy
annotation for this purpose, and SureLogic provides the @RegionLock
annotation. The field or method to which the @GuardedBy
annotation is applied can be accessed only when holding a particular lock. It may be an intrinsic lock or a dynamic lock such as java.util.concurrent.Lock
.
...
One issue with the @GuardedBy
annotation is that it fails to indicate when there is a relationship between the fields of a class. This limitation can be overcome by using the SureLogic @RegionLock
annotation, which 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 be accessed only when the lock is held. For example, the SimpleLock
locking policy indicates that synchronizing on the instance protects all of its state:
Code Block | ||
---|---|---|
| ||
@RegionLock("SimpleLock is this protects Instance") class Simple { ... } |
Unlike @GuardedBy
, the @RegionLock
annotation allows the programmer to give an explicit, and hopefully meaningful, name to the locking policy.
In addition to naming the locking policy, the @Region
annotation allows a name to be given to the region of the state that is being protected. That name makes it clear that the state and locking policy belong together, as demonstrated in the following example:
...
Typically, object construction is considered an exception to the locking policy because objects are thread-confined when they are created. An object is confined to the thread that uses the new
operator to create its instance. After creation, the object can be published to other threads safely. However, the object is not shared until the thread that created the instance allows it to be shared. Safe publication approaches discussed in TSM01-J. Do not let the this reference escape during object construction can be expressed succinctly with the @Unique("return")
annotation.
For example, in the following code, the @Unique("return")
annotation documents that the object returned from the constructor is a unique reference:
...
Annotating concurrent code helps document the design intent and can be used to automate the detection and prevention of race conditions and data races.
Automated Detection
Tool | Version | Checker | Description |
---|---|---|---|
The Checker Framework | 2.1.3 | GUI Effect Checker | Ensure that non-GUI threads do not access the UI which would crash the application (see Chapter 14) |
Bibliography
Item 70, "Document Thread Safety" | |
Java Concurrency in Practice | |
...