In this article, I would like to share a different approach on this subject that maybe is helpful for you, and also to talk about some particularities and misconceptions about . There`s very good material on this topic out there (like “Java Concurrency In Practice” [ ]). volatile JCIP I encourage you to read it ;) We will be talking about the semantics of as defined from Java 5 onwards (on previous Java versions, has no memory-consistency/state-visibility semantic). volatile volatile Java Concurrency: What is ? volatile Short answer: is a keyword we can apply to a to ensure that when one thread writes a value to that field, the value written is "immediately available" to any thread that subsequently reads it (visibility feature). volatile field Context When multiple threads need to interact with some shared data, there are three aspects to consider: : The effects of an action on the shared data by a thread should be observable by other threads (consistent views); Visibility : Execution order should be the same as statements appear in the source code (sequential consistency); Ordering No thread should interfere while another thread is executing some actions on the shared data. Atomicity : In the absence of necessary synchronizations, the compiler, Runtime, or processors may apply all sorts of optimizations, like and . Those optimizations can interact with incorrectly synchronized code in ways that are not immediately obvious when the source code is examined. Even if statements execute in the order of their appearance in a thread, caching can prevent the latest values from being reflected in the main memory. code reordering caching In order to prevent incorrect or unpredictable outcomes, we can use (some form of) . It is worth mentioning that synchronization doesn’t imply the use of the keyword (which is based on implicit/monitor/intrinsic internally); for example, and the keyword are also synchronization mechanisms. synchronization synchronized locks lock objects volatile In a multi-threaded context we usually must resort to explicit synchronization. But, in particular scenarios, there are some alternatives we could use that do not rely on synchronization, such as and . atomic methods immutable objects To illustrate the risks of a not correctly synchronized program, let’s consider the following example. This program could simply print 42, or even print 0, or even hang forever! { ready; number; { { (!ready) Thread.yield(); System.out.println(number); } } { ReaderThread().start(); number = ; ready = ; } } (from JCIP book) @NotThreadSafe public class NoVisibility private static boolean private static int private static class ReaderThread extends Thread public void run () while public static void main (String[] args) new 42 true Why could this happen? In Java, by default, a piece of code is assumed to be executed by only one thread. As we saw, the compiler, Runtime or processor could optimize things away, as long as we get the same result as with the original code. In a multi-threaded context is a developer's responsibility to use the appropriate mechanisms to correctly synchronize accesses to a shared state. In this particular example, the compiler could see that ‘ready’ is false (default value) and it never changes, so we would end up with an infinite loop. On the other hand, if the updated value for ‘‘ready’ is visible to ReaderThread before ‘number’ is (code reordering/caching), ReaderThread would see that ‘number’ is 0 (default value). The happens-before relationship From (section 17.4.5) : here “Two actions can be ordered by a happens-before relationship. If one action happens-before another, then the first is to and before the second (for example, the write of a default value to every field of an object constructed by a thread need not happen before the beginning of that thread, as long as no read ever observes that fact). More specifically, if two actions share a happens-before relationship, they do not necessarily have to appear to have happened in that order to any code with which they do not share a happens-before relationship." visible ordered This relationship is established by either: The construct (this also provides atomicity); synchronized The construct; volatile and methods; Thread.start() Thread.join() The methods of all classes in and its subpackages; java.util.concurrent ... To understand the importance of this relationship, let´s review the concept of data race. “A data race occurs when a variable is written to by at least one thread and read by at least another thread, and the reads and writes are not ordered by a relationship. A correctly synchronized program is one with no data races” (from ). More specifically: A correctly synchronized program is one whose sequentially consistent executions do not have any data races. happens-before here Volatile is a lightweight form of synchronization that tackles the visibility and ordering aspects. is used as a modifier. The purpose of is to ensure that when one thread writes a value to a field, the value written is "immediately available" to any thread that subsequently reads it. volatile volatile field volatile There’s a common misconception about the relationship between , on the one hand, and references and objects, on the other hand. In Java, there are and to those objects. We can think of references as “pointers” to objects. All variables (expect for primitives like boolean or long) contain references (object ). acts either on a primitive variable or on a reference variable. There’s no relation between and the object referred to by the (reference) variable. volatile objects references references volatile volatile Declaring a shared variable as . variables are not cached in registers or in caches where they are hidden from other processors, so a read of a variable always returns the most recent write by any thread. “The visibility effects of variables extend beyond the value of the variable itself. When thread A writes to a variable and subsequently thread B reads that same variable, the values of all variables that were visible to A prior to writing to the variable become visible to B after reading the variable” (from JCIP book). In order to guarantee that the results of one action are observable to a second action, then the first must the second. volatile ensures visibility volatile volatile volatile volatile volatile volatile volatile happen before also of accesses (accesses to the reference) by preventing the compiler and Runtime from reordering of code. The ability to perceive ordering constraints among actions is only guaranteed to actions . Under the hood, causes reads and writes to be accompanied by a special CPU instruction known as a . For more low-level details, you can check and . volatile limits reordering that share a happens-before relationship with them volatile memory barrier here here From a memory visibility perspective, writing a variable is like exiting a block and reading a variable is like entering a block. But note that doesn’t block as does. volatile synchronized volatile synchronized volatile synchronized An atomic action in concurrent programming is one that either happens completely, or it doesn't happen at all. There’s a correlation between and atomic actions: for non- 64-bit numeric (long and double) primitive variables, the JVM is free to treat a 64-bit read or write as two separate 32-bit operations. Using on those types ensures the read and write operations are atomic (accessing the rest of the variables and variables is atomic by default). accesses do not guarantee the atomicity of composite operations such as incrementing a counter. Compound operations on shared variables must be performed atomically to prevent data races and race conditions. No side effects of an atomic action are visible until the action is complete. volatile volatile volatile primitive reference volatile When to use volatile We can use variables only when all the following criteria are met (from JCIP book): volatile Writes to the variable do not depend on its current value, or you can ensure that only a single thread ever updates the value; The variable does not participate in invariants with other state variables Locking is not required for any other reason while the variable is being accessed. Some suitable scenarios are: When we have a simple flag (like a completion, interruption, or status flag) or other primitive item of data that is accessed (read) by multiple threads; When we have a more complex immutable object that is initialised by one thread and then accessed by others: here we want to update the reference to the immutable data structure and allow other threads to reference it. Considerations before using volatile Code that relies on variables for visibility of arbitrary state is more fragile and harder to understand than code that uses locking; volatile The semantics of is not strong enough to guarantee atomicity (atomic variables do provide atomic read-modify-write support and can often be used as “better variables”) volatile volatile Synchronization can introduce thread contention, which occurs when two or more threads try to access the same resource simultaneously. Examples Several of the following examples were taken from . You can find a lot more information about the examples and their internal working there. here 1) Incrementing a counter { Integer counter; { counter++; } } @NotThreadSafe public class UnsafeCounter private volatile public void increment () Even though it appears as a single operation, the “counter++;” statement is not atomic. It is actually a combination of three different operations: read, update, write. So, even making counter is not enough to avoid data races in a multi-threaded context: we may end up with volatile lost updates . Some alternatives: thread-safe synchronized , AtomicInteger . 2) Lazy-initialized Singleton Pattern { Singleton INSTANCE; { } { (INSTANCE != ) singleton; (Singleton.class) { (INSTANCE == ) INSTANCE = Singleton(); INSTANCE; } } } @ThreadSafe public class Singleton private static volatile private Singleton () Singleton public static getInstance () if null return synchronized if null new return In this case, not making INSTANCE could lead getInstance() to return a non-fully initialised object, breaking the Singleton pattern. Why? The assignment of the reference variable INSTANCE could happen the object is being initialised, exposing to other threads a (wrong) state that will end up changing, even if the object is immutable! Using , we can ensure a (see Pattern #2 ) of the object. volatile while volatile one-time safe publication here 3) Visibility { ready; number; { { (!ready) Thread.yield(); System.out.println(number); } } { ReaderThread().start(); number = ; ready = ; } } @NotThreadSafe public class NoVisibility private static boolean private static int private static class ReaderThread extends Thread public void run () while public static void main (String[] args) new 42 true In this case, making ‘ready’ is enough to make that class thread-safe because after main() updates ‘ready’, ReaderThread will see the new value for both ‘ready’ and ‘number’ ( relationship). But these kinds of constructs are fragile and we should avoid using them: let’s consider for example the case someone flips the two assignment statements. particular volatile happens-before Some alternatives: . thread-safe synchronized , Lock 4) Immutable collaborator { n; { .n = n; } } { Helper helper; { helper; } { helper = Helper(num); } } // Immutable Helper public final class Helper private final int public Helper ( n) int this // ... // Mutable Foo @NotThreadSafe final class Foo private Helper public getHelper () return public void setHelper ( num) int new Even though (mutable) Foo contains fields referring to only immutable objects (Helper), Foo is thread-safe. Mutable objects may not be fully constructed when their references are made visible. The reason is that, while the shared object (Helper) is immutable, the reference used to access it is itself shared and mutable. In the example, that means a separate thread could observe a stale reference in the field of the Foo object. One solution to that issue could be making the field of Foo ; guarantees an object is constructed properly before its reference is made visible. not helper helper volatile volatile Some alternatives: . thread-safe synchronized , AtomicReference 5) Compound operation { flag = ; { flag ^= ; } { flag; } } @NotThreadSafe final class Flag private volatile boolean true public void toggle () // Unsafe true public boolean getFlag () // Safe return As in the first example we saw, here we have a composite operation. is not enough in this case. volatile Some alternatives: . thread-safe synchronized , volatile + synchronized , ReadWriteLock , AtomicBoolean 6) Arrays { [] arr = [ ]; { arr[ ]; } { arr[ ] = n; } } @NotThreadSafe final class Foo private volatile int new int 20 public int getFirst () return 0 public void setFirst ( n) int 0 // … Here it is the array which is , not the array itself. reference volatile Some alternatives: thread-safe synchronized , AtomicIntegerArray . Conclusion variables are a weaker form of synchronization than locking, which in some cases are a good alternative to locking. If we follow the conditions for using we discussed before, maybe we can achieve thread-safety and better performance than with locking. However, code using is often more fragile than code using locking. volatile volatile volatile is not only an alternative to locking (in some cases); it has its own uses, also. can be combined with other synchronization mechanisms, but it is usually better to use only one mechanism on a particular shared state. volatile volatile As doesn’t block (unlike ), there is no possibility for deadlocks to occur. But, just as other synchronization mechanisms, the use of implies some performance penalties. On highly-contended contexts, using could be detrimental for performance. volatile synchronized volatile volatile Note that only applies to . It would not make sense to apply it to method parameters or local variables given all they are local/private to the executing thread. is related to object references, and not to operations on objects themselves. volatile fields volatile Using simple atomic variable access is more efficient than accessing these variables through code, but requires more care by the programmer to avoid memory consistency errors. Whether the extra effort is worthwhile depends on the size and complexity of the application. synchronized Volatile FAQs -Can we use volatile without using synchronized? Yes. -Can we use volatile together with synchronized? Yes. -Should we use volatile together with synchronized? It depends. -Does volatile apply to an object? No, it applies to an object or to a primitive type. reference -Does volatile tackle the atomicity aspect? No, but there is a special case for 64-bit primitives where it does. -Which is the difference between volatile and synchronized? Besides state visibility, keyword provides atomicity (through mutual exclusion) over a block of code. But the changes inside that block are visible to other threads only after the exit of that synchronized block. synchronized -Does volatile imply thread-safety? No at all: , , etc. are just synchronization mechanisms. As developers we must use them as appropriate, and those mechanisms depend on the case at hand. volatile synchronized -Does volatile improve thread performance? The opposite. The use of implies some performance penalties. volatile Bibliography https://jcip.net/ https://docs.oracle.com/javase/specs/jls/se8/html/jls-8.html#jls-8.3.1.4 https://docs.oracle.com/javase/specs/jls/se8/html/jls-8.html#jls-8.3.1.4 https://docs.oracle.com/javase/specs/jls/se8/html/jls-17.html#jls-17.4.2 https://download.oracle.com/otndocs/jcp/memory_model-1.0-pfd-spec-oth-JSpec/ https://resources.sei.cmu.edu/asset_files/TechnicalReport/2010_005_001_15239.pdf https://javarevisited.blogspot.com/2011/06/volatile-keyword-java-example-tutorial.html https://www.javamex.com/tutorials/synchronization_volatile.shtml https://www.cs.umd.edu/~pugh/java/memoryModel/DoubleCheckedLocking.html https://www.cs.umd.edu/~pugh/java/memoryModel/jsr-133-faq.html https://www.ibm.com/developerworks/java/library/j-jtp06197/index.html https://dzone.com/articles/demystifying-volatile-and-synchronized-again https://www.logicbig.com/tutorials/core-java-tutorial/java-multi-threading/java-memory-model.html https://docs.oracle.com/javase/7/docs/api/java/util/concurrent/locks/Lock.html https://docs.oracle.com/javase/tutorial/essential/concurrency/locksync.html https://docs.oracle.com/javase/tutorial/essential/concurrency/newlocks.html https://docs.oracle.com/javase/tutorial/essential/concurrency/immutable.html https://docs.oracle.com/javase/tutorial/essential/concurrency/sync.html https://docs.oracle.com/javase/tutorial/essential/concurrency/locksync.html https://docs.oracle.com/javase/tutorial/essential/concurrency/atomic.html https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/package-summary.html#MemoryVisibility https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/atomic/package-summary.html https://en.wikipedia.org/wiki/Memory_barrier https://developpaper.com/memory-barrier-and-its-application-in-jvm/ https://developpaper.com/memory-barrier-and-its-application-in-jvm-2/ https://www.infoq.com/articles/memory_barriers_jvm_concurrency/ https://dzone.com/articles/memory-barriersfences