为什么我们不需要带有 StampedLock 的 volatile?
Why don't we need volatile with StampedLock?
给定来自 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);
}
}
}
并且前提是 class 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 声明如下:
Memory Synchronization
All Lock implementations must enforce the same memory synchronization semantics as provided by the built-in monitor lock, as described in The Java Language Specification (17.4 Memory Model):
A successful lock operation has the same memory synchronization effects as a successful Lock action.
A successful unlock operation has the same memory synchronization effects as a successful Unlock action.
Unsuccessful locking and unlocking operations, and reentrant locking/unlocking operations, do not require any memory synchronization effects.
即使 StampedLock
没有实现 Lock
,它也有一个类似于 asReadLock()
的方法:
Returns a plain Lock view of this StampedLock in which the Lock.lock() method is mapped to readLock(), and similarly for other methods.
它 returns StampedLock
的内部 class ReadLockView
的实例,它是 Lock
的实际实现。
但是因为它只是一个委托者,这意味着原始方法必须创建内存屏障才能遵守 Lock
接口的内存同步强制执行。
给定来自 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);
}
}
}
并且前提是 class 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 声明如下:
Memory Synchronization
All Lock implementations must enforce the same memory synchronization semantics as provided by the built-in monitor lock, as described in The Java Language Specification (17.4 Memory Model):
A successful lock operation has the same memory synchronization effects as a successful Lock action. A successful unlock operation has the same memory synchronization effects as a successful Unlock action. Unsuccessful locking and unlocking operations, and reentrant locking/unlocking operations, do not require any memory synchronization effects.
即使 StampedLock
没有实现 Lock
,它也有一个类似于 asReadLock()
的方法:
Returns a plain Lock view of this StampedLock in which the Lock.lock() method is mapped to readLock(), and similarly for other methods.
它 returns StampedLock
的内部 class ReadLockView
的实例,它是 Lock
的实际实现。
但是因为它只是一个委托者,这意味着原始方法必须创建内存屏障才能遵守 Lock
接口的内存同步强制执行。