How volatile Impacts Java Threads: Experiments, JVM Modes, and Atomicity
This article demonstrates how the volatile keyword influences thread communication in Java, compares client and server JVM modes, analyzes resulting memory visibility and reordering effects, and explores atomicity limits with bytecode examples and practical recommendations.
1. Using volatile
The following Java program creates two threads: one writes to a shared static variable and the other reads it after a short sleep, illustrating the effect of the volatile keyword on visibility.
<code>public class VolatileUsedClass {
private static int sharedVar = 10;
public static void main(String[] args) throws Exception {
Thread thread1 = new Thread(new Runnable() {
@Override
public void run() {
try {
// modify the sharedVar, write first
TimeUnit.MICROSECONDS.sleep(500L);
sharedVar = 20;
System.out.printf("%s modify the shared var to %s ...\n", "thread-1", sharedVar);
} catch (Exception e) {
System.out.println(e);
}
}
});
Thread thread2 = new Thread(new Runnable() {
@Override
public void run() {
try {
// read the value, read last
TimeUnit.MICROSECONDS.sleep(505L);
System.out.printf("%s read the shared var %s \n", "thread-2", sharedVar);
} catch (Exception e) {
System.out.println(e);
}
}
});
thread2.start();
thread1.start();
thread1.join();
thread2.join();
System.out.println("finish the thread task...");
}
}
</code>Client mode (‑client)
Execution results without volatile (multiple runs) – see images.
Execution results with volatile (multiple runs) – see images.
Server mode (‑server)
Execution results without volatile (multiple runs) – see images.
Execution results with volatile (multiple runs) – see images.
Result analysis
The sleep calls enforce a write‑then‑read order.
Without volatile, the reading thread often sees a stale value cached locally, not the value from main memory.
With volatile, the reading thread observes the value written by the writer, demonstrating visibility and ordering guarantees.
In server mode, volatile can still lead to inconsistencies because the JIT may reorder bytecode for optimization.
2. Atomicity issues
All JDK documentation states that, except for long and double on 32‑bit CPUs, reads and writes of primitive types are atomic, and any volatile variable also provides atomic read/write.
For 64‑bit types on a 32‑bit processor, the JVM splits the operation into two 32‑bit writes, losing atomicity. The happens‑before rule ensures that reads of a volatile variable see the latest write, even for long/double.
Code example: volatile variable increment
<code>// Part of the write thread, assuming sharedVar is volatile
Thread t1 = new Thread(){
@Override
public void run() {
try {
// modify the sharedVar
TimeUnit.MICROSECONDS.sleep(500L);
sharedVar ++;
System.out.printf("%s modify the shared var with atomic %s ...\n", "thread-1", sharedVar);
} catch (Exception e) {
System.out.println(e);
}
}
};
</code>Running the program shows that the final value may not be the expected result because the increment operation consists of a read, an addition, and a write, which is not atomic despite the volatile qualifier.
Bytecode for a simple assignment (sharedVar = 20) shows a single store operation, which is atomic: <code>L3 LINENUMBER 102 L3 BIPUSH 20 INVOKESTATIC com/xiaokunliu/blogs/thread/volatile2code/VolatileUsedClass.access$002 (I)I POP L4 </code> Bytecode for the increment (sharedVar++) reveals an extra addition step, indicating a non‑atomic sequence: <code>L3 LINENUMBER 27 L3 INVOKESTATIC com/xiaokunliu/blogs/thread/volatile2code/VolatileUsedClass.access$000 ()I ICONST_1 IADD INVOKESTATIC com/xiaokunliu/blogs/thread/volatile2code/VolatileUsedClass.access$002 (I)I POP L4 </code> Summary of volatile usage Volatile guarantees visibility of writes to other threads and provides atomicity for single read/write actions, even for long/double. It does not make compound actions (e.g., ++, --, check‑then‑act) atomic; explicit locking is required for such cases. References: JVM Server vs Client Mode (https://javapapers.com/core-java/jvm-server-vs-client-mode/) and JDK concurrency atomicity tutorial (https://docs.oracle.com/javase/tutorial/essential/concurrency/atomic.html). Thank you for reading; please share if you found this helpful.
Xiaokun's Architecture Exploration Notes
10 years of backend architecture design | AI engineering infrastructure, storage architecture design, and performance optimization | Former senior developer at NetEase, Douyu, Inke, etc.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.