Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.
Comment: added read-write lock CS

Wiki Markup
Compound operations are operations that consist of more than one discrete operation. Expressions that include postfix and prefix increment ({{\+\+}}),  postfix or prefix decrement ({{\-\-}}), or compound assignment operators  always result in compound operations.  Compound assignment expressions includeuse operators such as {{*=, /=, %=, +=, -=, <<=, >>=, >>>=, ^=}} and {{|=}} \[[JLS 05|AA. Java References#JLS 05]\]. Compound operations on shared variables  must be performed atomically to prevent [data races|BB. Definitions#data race] and [race conditions|BB. Definitions#race conditions]. 

...

Execution of this code may result in a data race because the value of flag is read, negated, and written back.

...

Consider, for example, two threads that call toggle(). The effect of toggling flag twice is expected to restore it to its original value. However, however, the following scenario leaves flag in the incorrect state:

...

This code remains unsuitable for multithreaded use because declaring a variable as volatile does not guarantee that the atomicity of compound operations on that variable are performed atomically.

Compliant Solution (synchronization)

...

Wiki Markup
This approach may not be used when a getter method performs operations other than just returning the value of a {{volatile}} field that require, without having to use any synchronization. Unless read performance is critical, this technique may not offer significant advantages over synchronization \[[Goetz 06|AA. Java References#Goetz 06]\].

The volatile-read, synchronized-write pattern is also addressed in CON11-J. Do not assume that declaring an object reference volatile guarantees visibility of its members.

Compliant Solution (read-write lock)

This compliant solution uses a read-write lock to ensure atomicity and visibility.

Code Block
bgColor#ccccff

final class Flag {
  private boolean flag = true;
  private final ReadWriteLock lock = new ReentrantReadWriteLock();
  private final Lock readLock = lock.readLock();
  private final Lock writeLock = lock.writeLock();
  
  public synchronized void toggle() { 
    writeLock.lock();
    try {
      flag ^= true; // Same as flag = !flag;
    } finally {
      writeLock.unlock();
    }
  }

  public boolean getFlag() { 
    readLock.lock();
    try {
      return flag;
    } finally {
      readLock.unlock();
    }
  }
}

Wiki Markup
Read-write locks allow shared state to be accessed by multiple readers or a single writer, but never both. "In practice, read-write locks can improve performance for frequently accessed read-mostly data structures on multiprocessor systems; under other conditions they perform slightly worse than exclusive locks due to their greater complexity." \[[Goetz 06|AA. Java References#Goetz 06]\]. The suitability of read-write locks can be determined by profiling the application.

Compliant Solution (java.util.concurrent.atomic.AtomicBoolean)

...