Reputation: 819
I'm reading "Effective Java" and in the chapter where he's talking about threads, I stepped into this snippet:
private static int nextSerialNumber = 0;
public static int generateSerialNumber(){
return nextSerialNumber++;
}
A bit later, talking that snippet but in case there is no synchronization, he says:
More surprisingly, it is possible for one thread to call
generateSerialNumber
repeatedly, obtaining a sequence of serial numbers from zero to n, after which another thread callsgenerateSerialNumber
and obtains a serial number of zero. Without synchronization, the second thread might see none of the updates made by the first. This is a result of the aforementioned memory model issue.
I can't understand how this is possible.
For the thread to obtain "a sequence of serial numbers from zero to n", the increment must be done, otherwise the thread will read always the same value. If the increment is done, then the variable is set, because being an int
, the writing is atomic.
So, if the static variable is changed by a thread, although it might be the same one, another thread must be able to read that value. So how is possible that another thread, calling generateSerialNumber
, can obtain a serial number of zero?
Upvotes: 2
Views: 226
Reputation: 73538
because being an
int
, the writing is atomic.
That just means it's not possible for another thread to see a half-updated value, it's either the old one or the new one (in 64-bit systems this extends to long
variables).
It has nothing to do with the basic visibility problem meaning that unless your variable is volatile
or you're using synchronization
the value can and will be cached by different threads for performance purposes and you will see old cached values instead of up to date ones.
In addition, nextSerialNumber++;
is not atomic as it consists of read-update-write
steps, so making nextSerialNumber
volatile won't fix this code. The method needs to be synchronized
.
Upvotes: 4
Reputation: 108971
The current value of nextSerialNumber
might be cached in a cache local to the core, and updates to that value might also be cached for a while until they are flushed to main memory. So when using multiple threads, scheduled on different cores, they might have their own local cached version of nextSerialNumber
.
When not explicitly instructed, the code (and CPU) will assume it is fine to use this local cached version, and happily read and update the cached variable, while another thread scheduled on a different core will happily do the same with its own cached version.
When using concurrency primitives like synchronized
and volatile
, this changes. For a synchronized
-block (simplified) the Java implementation will make sure those values will be retrieved from main memory when first read and written back to main memory at the end of a synchronized
block, it does the same for volatile
variables, but then at each read and write.
In reality things are a bit more complicated, with happens-before relations between threads, etc. But basically, your question boils down to "blame caching".
Upvotes: 2