Preamble: CPU Memory Model

Each core of a CPU has registers, several layers of cache, usually a shared cache and access to the main memory.

This architecture deliberately creates redundancy and can introduce inconsistency. A variable may have been copied to a register or cache and may have a different value than the current value in memory.

An example scenario is: Thread A executes on Core1 and copies a value of a variable to cache, Thread B executes on Core2 and copies a value of a variable to cache. Thread A updates the variable and writes it back to cache (and RAM). Thread B reads back from its own cache and has an older version of a variable.

However, CPUs have systems for flushing the cache and registers where applicable to memory and refreshing data back from memory.

volatile keyword

Java provides us with a keyword volatile which is a signal that variables must be flushed to main memory when changed and also read back from main memory when accessed.

Visibility Guarantee

When using synchronized blocks, Java gives you a visibility guarantee that all of the variables visible to the thread will be refreshed from main memory, and when the block is exited all variables will be flushed back to main memory.

Instruction reordering

The compiler and CPU may choose to reorder instructions to improve performance during execution, however this may change logic when working with threads as seemingly independent events.