Java – Use of volatile variables in concurrency/multi-threading

The problem –

  • Computers typically have multiple CPU Cores (or CPUs). Each Core has one or more dedicated local cache (L1 and L2 cache)
  • Computers also have a main memory (RAM). This memory is shared by all Cores
  • When a thread gets a chance to run, it typically runs on one of the available Cores (the OS controls which process + thread gets to run on which core)
  • When a running thread sets a variable to some value, it makes that change in the local cache of its’ CPU core. This value may or may NOT get flushed to the main memory. This flexibility allows compilers/JVMs to make performance improvements
  • If another thread, running on a different core, reads the value of that variable, it may or may not see the latest value
  • So if you have a variable which supposed to be used/shared by multiple threads (e.g. a boolean flag to signal something across threads), the default behavior mentioned above may cause strange and serious issues

CPU L1 Cache and Main Memory

Why does Java allow for this problem to happen?

  • For performance improvement, compilers are allowed to reorder program instructions as long as long as the outcome doesn’t get affected
  • This does NOT take multi-threading into account. Compilers can re-order instructions as if only a single thread would execute the code
  • So when a thread is executing something (e.g. the whole program or a method), compilers are free to reorder instructions as long as that same thread doesn’t get impacted. So if a thread made a change to some variable (e.g. i++), then any subsequent instructions executed by the same thread must be able to see the incremented value. It is okay if other threads don’t see the changes made by this thread
  • This is generally acceptable as most programs are either not multi-threaded OR they don’t have threads sharing variables. Even if they had a problematic piece of code, the error may not come-up (compilers/JVMs may perform these “reorder instructions” tricks, but they may not do that in various scenarios)

How volatile solves the problem?

  • If you mark a variable as volatile, then the JVM makes sure that the latest value of that variable is visible to ALL threads
  • For simplicity, the flow can be visualized as follows –
    • When any thread changes the value of a volatile variable, it flushes its local changes to the main memory

Memory effects of writing a volatile

    • When any thread is about to read a volatile variable, it invalidates its own cache and reads all variable values from the main memory

Memory effects of reading a volatile

  • In fact, the JVMs provides an even stronger guarantee
    • Lets say thread A did the following –
      • Changed the value of a non-volatile variable (e.g. i = 10;)
      • Changed the value of a volatile variable
    • When a thread B reads the same volatile variable, it sees the latest value. In addition, it also sees ALL changes made by thread A prior to setting the volatile variable. This is called a “happens-before” relationship. So thread B is guaranteed to see the value of i as 10
    • JVM guarantees that all changes made by thread A before the write to the volatile variable “happen-before” (execute before) the read operation performed by thread B

volatile write followed by a read

Performance implications of volatile

  • Generally, on modern architectures, volatile reads add no significant cost
  • Volatile writes are heavier than normal writes as the entire CPU cache has to be flushed to the main memory. However, volatiles are cheaper than synchronization (synchronization can also be used for ensuring variable value visibility across threads)


Found this useful? Please leave a Reply :)