volatile is a variable modifier that ensures visibility of changes to this variable by all threads. If a variable is declared as volatile, its reading and writing occur directly from the main memory, bypassing the local cache of threads. This prevents caching of values locally within the thread.
synchronized is a keyword that provides not only visibility but also mutual exclusion: only one thread can execute a synchronized block at a time for a single object.
You should use volatile variables for simple flags and counters when:
Example of using volatile:
class Flag { private volatile boolean running = true; public void stop() { running = false; } public void loop() { while (running) { // ... } } }
Can volatile be used to ensure the atomicity of incrementing a variable?
Answer:
No, volatile does not ensure the atomicity of operations. For example, counter++ is not atomic even if counter is declared as volatile because the operation involves reading, modifying, and writing (multiple actions) that can be interrupted by another thread. Atomicity is ensured using synchronized or classes from the java.util.concurrent.atomic package.
Example:
class Counter { private volatile int count = 0; public void increment() { count++; // NOT an atomic operation! } }
Story
In an online store project, the thread completion flag for order processing was implemented without volatile. As a result, one of the threads "stuck" in an infinite loop, as it did not see the variable change made from another thread. Diagnosis took several days.
Story
In a financial system, a developer used volatile int for a transaction counter. Under peak loads, the number of transactions began to "disappear." The reason was the loss of atomicity during complex increment operations.
Story
Developers confused volatile and synchronized, trying to use volatile to ensure mutual exclusion access to critical sections, which led to data races and elusive bugs in a multithreaded application.