Chapter 3. Sharing Objects

  • Synchronized is not only about atomicity or demarcating "critical sections", it also has another significant, and subtle, aspect: memory visibility.
  • When a thread modifies the state of an object, other threads can actually see the changes that were made. But without synchronization, this may not happen.
  • You can ensure that objects are published safely either by using explicit synchronization or by taking advantage of the synchronization built into library classes.

3.1. Visibility

Locking is not just about mutual exclusion; it is also about memory visibility. To ensure that all threads see the most up to date values of shared mutable variables, the reading and writing threads must synchronize on a common lock.

  • No guarantee that the reading thread will see a value written by another thread on a timely basis, or even at all. In order to ensure visibility of memory writes across threads, you must use synchronization.
  • In the absence of synchronization, the compiler, processor, and runtime can do some downright weird things to the order in which operations appear to execute. Attempts to reason about the order in which memory actions "must" happen in insufficiently synchronized multithreaded programs will almost certainly be incorrect.
    • a thread can see an up to date value of one variable but a stale value of another variable that was written first. (Stale Data)
  • Non atomic 64 bit Operations
    • The Java Memory Model requires fetch and store operations to be atomic, but for nonvolatile long and double variables, the JVM is permitted to treat a 64 bit read or write as two separate 32 bit operations.
    • It is not safe to use shared mutable long and double variables in multithreaded programs unless they are declared volatile or guarded by a lock.
  • requiring all threads to synchronize on the same lock when accessing a shared mutable variable to guarantee that values written by one thread are made visible to other threads. Otherwise, if a thread reads a variable without holding the appropriate lock, it might see a stale value.

  • Volatile Variables

    Locking can guarantee both visibility and atomicity; volatile variables can only guarantee visibility.

    • When a field is declared volatile, the compiler and runtime are put on notice that this variable is shared and that operations on it should not be reordered with other memory operations. Volatile variables are not cached in registers or in caches where they are hidden from other processors, so a read of a volatile variable always returns the most recent write by any thread.
    • accessing a volatile variable performs no locking and so cannot cause the executing thread to block, making volatile variables a lighter weight synchronization mechanism than synchronized.
      • Volatile reads are only slightly more expensive than nonvolatile reads on most current processor architectures.
    • The visibility effects of volatile variables extend beyond the value of the volatile variable itself. When thread A writes to a volatile variable and subsequently thread B reads that same variable, the values of all variables that were visible to A prior to writing to the volatile variable become visible to B after reading the volatile variable.
    • The most common use for volatile variables is as a completion, interruption, or status flag.
      volatile boolean asleep;
      ...
      while (!asleep)
         countSomeSheep();
      

3.2 Publication and Escape

  • Publishing an object means making it available to code outside of its current scope, such as
  • An object that is published when it should not have been is said to have escaped.
    • Once an object escapes, you have to assume that another class or thread may, maliciously or carelessly, misuse it.
  • we do want to publish an object for general use, but doing so in a thread safe manner may require synchronization. Publishing internal state variables can compromise encapsulation and make it more difficult to preserve invariants; publishing objects before they are fully constructed can compromise thread safety.

Usual cases to publish an object

  • by storing a reference to it where other code can find it,
  • returning it from a non private method, or
  • passing it to a method in another class.
  • any object that is reachable from a published object by following some chain of non private field references and method calls has also been published.
  • Passing an object to an alien method must also be considered publishing that object.
    • From the perspective of a class C, an alien method is one whose behavior is not fully specified by C.
    • you can't know what code will actually be invoked, you don't know that the alien method won't publish the object or retain a reference to it that might later be used from another thread.
  • publish an inner class instance can cause an object or its internal state be published, because inner class instances contain a hidden reference to the enclosing instance.

results matching ""

    No results matching ""