Concurrency
This lesson lists gotchas of Java concurrency.
We'll cover the following...
Notes on Concurrency
Synchronizing only the write path isn't sufficient for thread-safety. In fact, synchronization has no effect unless both read and write operations are synchronized. The class below shows such a scenario, where the read path isn't synchronized and a thead may never see the updated value of the flag set by another thread because of how the Java's memory model works.
Bad Practice
class Unsafe { Boolean flag; public synchronized void setFlag(Boolean flag) { this.flag = flag; } // Read path is unsynchronized and a thread reading // the flag may never see an updated value by another thread public Boolean getFlag() { return flag; } }
The
volatile
modifier doesn't perform mutual exclusion, but it guarantees that any thread that reads a volatile field will see the most recently written value. The above class can be fixed as follows:class Unsafe { volatile Boolean flag; public synchronized void setFlag(Boolean flag) { this.flag = flag; } // No synchronization but latest value is read public Boolean getFlag() { return flag; } }
It is possible that a thread invokes
setFlag()
but before it can complete another thread comes along and reads the flag value. However, the read or write to a variable is atomic. It can't happen that half the bytes of a variable are written by thread#1 and the other half are written by thread#2 resulting in an arbitrary value being stored. So whenever a thread concurrently reads a variable without synchronization, it is guaranteed to see a value that was written to the variable by another thread. One caveat is that this guarantee doesn't apply to type long or double unless they are marked volatile. This is because writes to long or double can translate to multistep operation at the machine code level.Be wary of non-atomic operations. Consider the snippet below:
private static volatile int nextID = 0;