The thread-safety can be expressed like "if the object of the class behaves exactly in the same way in multithreaded environment as in single-threaded environment, then it is thread-safe", or maybe instead "behaves exactly as" we could say "behaves correctly".
In your case correct behavior means that after calling setValue(N)
, a following getValue()
returns N
. In case you wouldn't use the volatile
modifier, this would be true only in single-threaded environment: if you call setValue(N)
on Thread1 then after you call getValue()
on Thread2, then it won't necessarily return N
. The problem is that Thread1 and Thread2 may be executed on separate CPU cores, and CPU cores usually don't read/write directly the shared memory: the two processors can have separate CPU caches, and the setValue(N)
call may modify only the CPU-cached copy of the value member, therefore this change won't be immediately visible for the RAM. Furthermore, even if N
is already available in the shared memory, the getValue()
may read a cached - but obsolete - value from the second processor cache. Therefore the value change you perform on Thread1 may not be immediately visible on Thread2, so getValue()
may return a deprecated value.
The volatile modifier solves it with its following 2 properties:
- after writing a volatile value it won't only be stored in the CPU cache, but immediately flushed into the shared RAM
- reading a volatile value will always read it from the memory, never from the CPU cache.