Please check the sample code below:
private volatile int fActiveTestDeathCount; synchronized void waitUntilFinished() { while (fActiveTestDeathCount < testCount()) { try { wait(); } catch (InterruptedException e) { return; // ignore } } }
The above code can run normally, which is very easy to understand, but why the following code can also run normally?
private int fActiveTestDeathCount; synchronized void waitUntilFinished() { while (fActiveTestDeathCount < testCount()) { ... } }
I think that in a multi-threaded execution environment, each thread copies the fActiveTestDeathCount variable. If there is no additional volatile modifier, this variable is invisible to other threads, and the program should not work, but this is not the case.
I try to analyze whether it is that after the thread is awakened, it will automatically invalidate the variables in the thread’s working memory, and then synchronize the data from the main memory? But this requires the support of memory barriers, which makes me a little confused.
Advertisement
Answer
Here volatile
is neither required nor sufficient.
Why is it not sufficient? Because it won’t stop a thread from checking fActiveTestDeathCount
right before it is modified by another thread and then irrevocably deciding to wait
for something that happened right after it checked.
Why is it not required? Because synchronized
already establishes a “happens before” relationship among any two threads accessing fActiveTestDeathCount
.
The whole point of wait
is to provide an atomic “unlock and wait” operation to solve precisely this race condition.