【发布时间】:2018-02-07 22:07:58
【问题描述】:
给出来自 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 时,是否会建立任何类型的内存屏障?
任何人都可以从文档中引用关于这方面的内容吗?
【问题讨论】:
-
一般而言获取和释放锁确实会创建内存屏障,因此对锁内完成的变量的任何访问(无论是
StampedLock还是其他)都将确保可见性。 -
docs.oracle.com/javase/8/docs/api/index.html?java/util/… 向下滚动到“内存一致性属性”以获得保证和重要位 - “java.util.concurrent 及其子包中所有类的方法将这些保证扩展到更高级别的同步. 特别是:[...]"
-
@pvg 感谢您引用文档,它应该用于正式证明。
-
@DmitryGorbunov 对此有一些元答案,因为这个问题有很多关于 SO 的变体,都略有不同,涉及不同类的 java.util.concurrent - 全套保证并且对 JMM/JLS 的引用很长,并且它们在每个类的文档中都没有重复,更不用说每个方法,但它们在包(和子包文档)中进行了概述。所以如果你遇到这样的事情,那是第一个(不是完全显而易见的)要检查的地方。
标签: java multithreading java-8 volatile memory-barriers