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

07-24 12:51