为何无同步时线程更新不可见?《Effective Java》线程序列号问题咨询
Great question—this gets to a key distinction between atomicity and visibility in Java's memory model, which is super easy to mix up. Let's break this down step by step:
First: Clarify the Two Critical Concepts
You’re right that writes to int variables are atomic, but atomicity only guarantees a write operation completes without being split into smaller, interruptible parts. It does not guarantee that other threads will see that write immediately. That’s where visibility comes in—the rule that dictates when changes to a shared variable become visible to other threads.
How Java's Memory Model Explains the Behavior
Java doesn’t force threads to read/write directly to shared main memory. Instead:
- Each thread maintains its own working memory (a local cache of variables it’s actively using).
- When a thread reads a variable, it copies the current value from main memory to its working memory.
- When a thread writes a variable, it updates its working memory first—and there’s no guarantee when that update gets flushed back to main memory (it could be seconds later, or never, if the thread exits before flushing).
Applying This to Your Code Snippet
Let’s walk through the scenario you described:
- Thread A calls
generateSerialNumber()repeatedly, getting 0, 1, ..., n.- Each time, Thread A reads
nextSerialNumberfrom its local working memory, increments it, and writes the new value back to its own working memory. - Without synchronization, Thread A never flushes these updates to main memory. The main memory still holds the initial value
0.
- Each time, Thread A reads
- Thread B calls
generateSerialNumber()next.- Thread B reads
nextSerialNumberdirectly from main memory (still0), increments it to1in its own working memory, and returns0.
- Thread B reads
So even though Thread A’s operations were atomic, Thread B never saw any of its updates because those changes were stuck in Thread A’s local cache.
Why Volatile Fixes This
If you mark nextSerialNumber as volatile:
private static volatile int nextSerialNumber = 0;
- It forces every read of the variable to come directly from main memory (no local caching).
- It forces every write to the variable to be immediately flushed to main memory.
This ensures visibility across threads, so Thread B would see the updated value from Thread A instead of the initial 0.
A Quick Side Note on Atomicity
Also, keep in mind that nextSerialNumber++ isn’t fully atomic—it’s a read-modify-write operation. Even with visibility, without additional synchronization (like synchronized methods or AtomicInteger), you could still get duplicate serial numbers if two threads read the same value at the same time. But your specific question is about visibility, which is why Thread B can read 0 after Thread A’s updates.
内容的提问来源于stack exchange,提问作者acejazz

