Immutability

We have seen how to implement thread-safe access to shared resources in synchronized code that uses intrinsic locks (§22.4, p. 1387). However, if the shared resource is an immutable object, thread-safety comes for free.

An object is immutable if its state cannot be changed once it has been constructed. Since its state can only be read, there can be no thread interference and the state is always consistent.

Examples of immutable classes in the Java SE Platform API and designing immutable classes are discussed in §6.7, p. 356.

Volatile Fields

To optimize the execution of compiled code, the compiler performs optimizations on the bytecode. Typical optimizations are instruction reordering, method call inlining, and loop optimizations. For multithreaded applications, thread-specific optimizations can also be performed. It is quite common for threads to create their own private cached copies of variables (called thread-local variables) for performance reasons. Values of thread-local variables are synchronized with the values of their master variables in main memory at various times—for example, when a thread enters or exits synchronized code. Declaring a field as volatile informs the compiler that the field will be modified by different threads, and all reads and writes to the field should be reflected in the master copy in main memory. Without the volatile modifier, there is no guarantee that the latest value in a shared field will be visible to the different threads. The visibility of a shared volatile field is guaranteed by the following rule:

  • Volatile Field Rule: A write to a volatile field happens-before every subsequent read of that field (§22.5, p. 1414).

The rule implies that a read on a volatile field will always see the value from the latest write on the volatile field. Without the volatile modifier, the threads may see different values in the shared field. The class NonVolatileDemo in Example 23.8 illustrates the potential problem that can occur with visibility when different threads share data. The boolean field stopThread at (1) is used in the run() method at (3) of each child thread spawned in the loop at (2) to stop the thread when the field value becomes true. The main thread sets this value after sleeping for a little while. If the threads are using thread-local copies of the field stopThread, there is no guarantee when these threads will get to see the updated value, and therefore, might continue executing forever.

The class VolatileDemo in Example 23.8 declares the field stopThread at (6) to be volatile. This is a typical use of the volatile modifier: to flag a condition to different threads. When the value is updated in the main thread, the child threads will see the updated value on the next read of the field, and are guaranteed to terminate.

Example 23.8 Visibility of Shared Data

Click here to view code image

package safe;
/** Potential problem with visibility of shared data. */
public class NonVolatileDemo {
  private static boolean stopThread = false;             // (1)
  public static void main(String[] args) throws InterruptedException {
    for (int i = 0; i < 2; i++) {                        // (2)
      new Thread(() -> {
        while (!stopThread) {                            // (3)
          System.out.println(Thread.currentThread().getName()
                             + “: Get me out of here!”);
        }
      }, “T” + i).start();
    }
    Thread.sleep(1);                                     // (4)
    stopThread = true;                                   // (5)
  }
}

Click here to view code image

package safe;
/** Volatile field to guarantee visibility of shared data. */
public class VolatileDemo {
  private static volatile boolean stopThread = false;    // (6)
  // The main() method remains the same as in the NonVolatileDemo class.

}

Probable output from each of the programs: …
T0: Get me out of here!
T0: Get me out of here!
T0: Get me out of here!
T1: Get me out of here!