给出了来自Oracle文档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
的指令都将具有完整的内存屏障。