[英]Why don't we need volatile with StampedLock?
給出來自Oracle docs的代碼示例https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/locks/StampedLock.html
class Point {
private double x, y;
private final StampedLock sl = new StampedLock();
void move(double deltaX, double deltaY) { // an exclusively locked method
long stamp = sl.writeLock();
try {
x += deltaX;
y += deltaY;
} finally {
sl.unlockWrite(stamp);
}
}
double distanceFromOrigin() { // A read-only method
long stamp = sl.tryOptimisticRead();
double currentX = x, currentY = y;
if (!sl.validate(stamp)) {
stamp = sl.readLock();
try {
currentX = x;
currentY = y;
} finally {
sl.unlockRead(stamp);
}
}
return Math.sqrt(currentX * currentX + currentY * currentY);
}
void moveIfAtOrigin(double newX, double newY) { // upgrade
// Could instead start with optimistic, not read mode
long stamp = sl.readLock();
try {
while (x == 0.0 && y == 0.0) {
long ws = sl.tryConvertToWriteLock(stamp);
if (ws != 0L) {
stamp = ws;
x = newX;
y = newY;
break;
}
else {
sl.unlockRead(stamp);
stamp = sl.writeLock();
}
}
} finally {
sl.unlock(stamp);
}
}
}
並且只要可以從不同的線程調用Point類的所有方法:
為什么我們不需要將字段x和y聲明為volatile?
是否保證執行Point#moveIfAtOrigin
方法的代碼在獲取StampedLock#readLock
后總能看到x和y字段的最新變化?
當我們調用StampedLock#writeLock
, StampedLock#readLock
時,是否存在任何類型的內存屏障?
任何人都可以指出有關該文檔的引用嗎?
我不知道為什么在文檔中沒有明確引用 - 可能是因為它有點隱含,但內部有一個Unsafe.compareAndSwapLong
轉換為LOCK CMPXCHG
,它在x86
有full memory barrier
(我假設像這是在其他平台上完成的); 所以沒有必要確實那些volatile
。
實際上, x86
上有lock
任何指令都會有一個完整的內存屏障。
Lock
接口的Javadoc聲明如下:
內存同步
所有Lock實現必須強制執行內置監視器鎖提供的相同內存同步語義,如Java語言規范(17.4內存模型)中所述:
成功的鎖定操作具有與成功鎖定操作相同的內存同步效果。 成功解鎖操作具有與成功解鎖操作相同的內存同步效果。 不成功的鎖定和解鎖操作以及重入鎖定/解鎖操作不需要任何內存同步效果。
即使StampedLock
沒有實現Lock
,它也有像asReadLock()
這樣的方法:
返回此StampedLock的普通Lock視圖,其中Lock.lock()方法映射到readLock(),對於其他方法也是如此。
它返回StampedLock
的內部類ReadLockView
的實例,它是Lock
的實際實現。
但是因為它只是一個委托者,這意味着原始方法必須創建內存屏障才能符合Lock
接口的內存同步實施。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.