I read about some of the details of implementation of ReentrantLock in "Java Concurrency in Practice", section 14.6.1, and something in the annotation makes me confused:
Because the protected state-manipulation methods have the memory semantics of a volatile read or write and ReentrantLock is careful to read the owner field only after calling getState and write it only before calling setState, ReentrantLock can piggyback on the memory semantics of the synchronization state, and thus avoid further synchronization see Section 16.1.4.
the code which it refers to:
protected boolean tryAcquire(int ignored) {
final Thread current = Thread.currentThread();
int c = getState();
if (c ==0) {
if (compareAndSetState(0, 1)) {
owner = current;
return true;
}
} else if (current == owner) {
setState(c+1);
return true;
}
return false;
}
And I believe this is the simplified code of the nonfairTryAcquire
in the ReentrantLock.Sync
.
final boolean nonfairTryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
if (c == 0) {
if (compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(current);
return true;
}
}
else if (current == getExclusiveOwnerThread()) {
int nextc = c + acquires;
if (nextc < 0) // overflow
throw new Error("Maximum lock count exceeded");
setState(nextc);
return true;
}
return false;
}
So, the baffling part is how the setting of owner
, which is merely a plain instance variable in AbstractOwnableSynchronizer
, becomes visible to else if (current == owner)
in other threads. Indeed, the read of owner
is after the calling of getState()
(and the state
is a volatile
qualified variable of AQS
), but after the setting of owner
, there's nothing (can impose synchronization semantics) at all. Data race happens?
Well, in light of the authority of this book and the thoroughly tested code, two possibilities come to my mind:
The full barrier (be it mfence or 'lock'ed instruction) before the setting
owner = current
does the hidden work. But from I've learned from several famous articles, the full barrier cares more about the writes before it as well as the reads after it. Well, if this possibility holds true, then some sentences in "JCIP" might be inappropriately stated.I notice that 'geographically' the
setState(c+1)
really comes afterowner = current
in the code snippet, although it's in another branch of if-else. If what the comments says is the truth, does it mean that the barrier inserted bysetSate(c+1)
can impose synchronization semantics onowner = current
in another branch?
I'm a novice in this area, and several great blogs help me a lot in understanding what's underlying the JVM(no ordering):
- http://mechanical-sympathy.blogspot.com/
- http://preshing.com/
- http://bartoszmilewski.com
- http://psy-lob-saw.blogspot.com/
as well as the always magnificent: http://g.oswego.edu/dl/jmm/cookbook.html
After doing my homework and searching the internet, I fail to come to a satisfying conclusion.
Pardon me if this is too wordy or unclear(English is not my mother tongue). Please help me with this, anything related is appreciated.