Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

...

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
bgColor#ccccff
 @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 that upon which the promise of thread-safety is predicated upon.

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; after they are fully constructed, they may be published via a volatile 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 by Collections.synchronizedMap.

The @NotThreadSafe annotation is applied to classes that are not thread-safe. Many classes fail to document whether or not 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.

...

It is important to document all the locks that are being used to protect shared state. According to Goetz and colleagues [Goetz 2006, p. 28],

For each mutable state variable that may be accessed by more than one thread, all accesses to that variable must be performed with the same lock held. In this case, we say that the variable is guarded by that lock. (p. 28)

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 only be accessed only when holding a particular lock. It may be an intrinsic lock or a dynamic lock such as java.util.concurrent.Lock.

For example, the following MovablePoint class implements a movable point that can remember its past locations using the memo array list.:

Code Block
bgColor#ccccff
@ThreadSafe
public final class MovablePoint {

  @GuardedBy("this")
    double xPos = 1.0;
  @GuardedBy("this")
    double yPos = 1.0;
  @GuardedBy("itself")
    static final List<MovablePoint> memo = new ArrayList<MovablePoint>();

  public void move(double slope, double distance) {
    synchronized (this) {
      rememberPoint(this);
      xPos += (1 / slope) * distance;
      yPos += slope * distance;
    }
  }

  public static void rememberPoint(MovablePoint value) {
    synchronized (memo) {
      memo.add(value);
    }
  }
}

The @GuardedBy annotations on the xPos and yPos fields indicate that access to these fields is protected by holding a lock on this (also done in the move() method, which modifies these fields). The The @GuardedBy annotation on the memo list indicates that a lock on the ArrayList object protects its contents (also done in the rememberPoint() method).

One issue with the the @GuardedBy annotation is that it fails to indicate that 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.

...

Code Block
bgColor#ccccff
 @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:

...

In this example, a locking policy named StateLock is used to indicate that locking on stateLock protects the named AircraftPosition region, which includes the mutable state used to represent the position of the aircraft.

Construction of Mutable Objects

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 rule  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.

...

For example, the following annotations express the design intent that a program has, at most, one Abstract Window Toolkit (AWT) event dispatch thread and several compute threads, and that the compute threads are forbidden to handle AWT data structures or events:

...

According to Goetz and colleagues [Goetz 2006, p. 395],

A state-dependent class should either fully expose (and document) its waiting and notification protocols to subclasses, or prevent subclasses from participating in them at all. (This is an extension of "design and document for inheritance, or else prohibit it" [EJ Item 15].) At the very least, designing a state-dependent class for inheritance requires exposing the condition queues and locks and documenting the condition predicates and synchronization policy; it may also require exposing the underlying state variables. (The worst thing a state-dependent class can do is expose its state to subclasses but not document its protocols for waiting and notification; this is like a class exposing its state variables but not documenting its invariants.) (p. 395)

Wait-notify protocols should be documented adequately. Currently, we are not aware of any annotations for this purpose.

...

Bibliography

[Bloch 2008]

Item 70: , "Document thread safetyThread Safety"

[Goetz 2006]

 

[Sutherland 2010]

 

 

...