什么是Java中的StampedLock?

时间:2014-09-29 07:04:00

标签: java multithreading java-8

我正在研究Java代码,我需要在其中实现线程。我正在通过JAVA 8 API,我开始了解Stamped Locks。谁能告诉我为什么在多线程中使用StampedLocks?

提前致谢。

4 个答案:

答案 0 :(得分:20)

StampedLock是使用ReadWriteLock(由ReentrantReadWriteLock实现)的替代方法。 StampedLock和ReentrantReadWriteLock之间的主要区别在于:

  • StampedLocks允许对读取操作进行乐观锁定
  • ReentrantLocks是可重入的(StampedLocks不是)

因此,如果你有一个你有争用的场景(否则你可以使用synchronized或简单的Lock)和更多的读者而不是作家,使用StampedLock可以显着提高性能。

但是,在得出结论之前,您应该根据具体的用例来衡量效果。

Heinz Kabutz撰写了关于StampedLocks in his newsletter的文章,他还撰写了a presentation about performance

答案 1 :(得分:5)

java.util.concurrent.locks.StampedLock的API文档说:

  

StampedLocks设计用作开发线程安全组件的内部实用程序。它们的使用依赖于对它们所保护的数据,对象和方法的内部属性的了解。它们不是可重入的,因此锁定的主体不应该调用可能尝试重新获取锁的其他未知方法(尽管您可以将戳记传递给可以使用或转换它的其他方法)。读锁定模式的使用依赖于相关的代码部分是无副作用的。未经验证的乐观读取部分无法调用未知容忍潜在不一致的方法。标记使用有限表示,并且不是加密安全的(即,可以猜测有效标记)。印花值可在连续操作一年后(不迟于一年)后回收。未经使用或验证超过此期限而持有的印章可能无法正确验证。 StampedLocks是可序列化的,但总是反序列化为初始解锁状态,因此它们对远程锁定没有用。

e.g。 -

 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);
     }
   }
 }

答案 2 :(得分:1)

StampedLock支持读写锁。与ReadWriteLock相比,StampedLock的锁定方法返回由long值表示的戳记。您可以使用这些标记释放锁定或检查锁定是否仍然有效。另外,标记锁支持另一种称为乐观锁定的锁定模式。

ExecutorService executor = Executors.newFixedThreadPool(2);
        Map<String, String> map = new HashMap<>();
        StampedLock lock = new StampedLock();

        executor.submit(() -> {
            long stamp = lock.writeLock();
            try {
                Thread.sleep(100);
                map.put("test", "INDIA");
            } catch (Exception e) {
            } finally {
                lock.unlockWrite(stamp);
            }
        });

        Runnable readTask = () -> {
            long stamp = lock.readLock();
            try {
                System.out.println(map.get("test"));
                Thread.sleep(100);
            } catch (Exception e) {
            } finally {
                lock.unlockRead(stamp);
            }
        };

        executor.submit(readTask);
        executor.submit(readTask);

通过readLock()或writeLock()获取读或写锁定返回一个标记,该标记稍后用于在finally块中解锁。请记住,加盖锁不会实现折返特性。每次锁定调用都会返回一个新的标记,如果没有锁定可用则会阻塞,即使同一个线程已经拥有锁定。所以你必须特别注意不要遇到死锁。

executor.submit(() -> {
            long stamp = lock.tryOptimisticRead();
            try {
                System.out.println("Optimistic Lock Valid: " + lock.validate(stamp));
                Thread.sleep(100);
                System.out.println("Optimistic Lock Valid: " + lock.validate(stamp));
                Thread.sleep(1000);
                System.out.println("Optimistic Lock Valid: " + lock.validate(stamp));
            } catch (Exception e) {
            } finally {
                lock.unlock(stamp);
            }
        });

        executor.submit(() -> {
            long stamp = lock.writeLock();
            try {
                System.out.println("Write Lock acquired");
                Thread.sleep(100);
            } catch (Exception e) {
            } finally {
                lock.unlock(stamp);
                System.out.println("Write done");
            }
        });

通过调用tryOptimisticRead()获取乐观读锁定,无论锁定是否实际可用,它都会在不阻塞当前线程的情况下返回戳记。如果已经有写锁定激活,则返回的戳记等于零。您始终可以通过调用lock.validate(stamp)来检查戳记是否有效。

答案 3 :(得分:0)

除了@assylias answer

StampedLock支持使用方法tryConvertToWriteLock(long)将readLock升级为writeLock:

java doc

  

此类还支持有条件提供的方法   三种模式之间的转化。例如方法   tryConvertToWriteLock(long)尝试“升级”模式,返回一个   如果(1)已经处于写入模式(2)处于读取模式,则有效的写入标记   并且没有其他阅读器,或者(3)处于乐观模式并且处于锁定状态   可用。这些方法的形式旨在帮助减少   在基于重试的设计中可能会发生的一些代码膨胀。

代码示例:

class Point {
   private double x, y;
   private final StampedLock sl = new StampedLock();

   // an exclusively locked method
   void move(double deltaX, double deltaY) {
     long stamp = sl.writeLock();
     try {
       x += deltaX;
       y += deltaY;
     } finally {
       sl.unlockWrite(stamp);
     }
   }

   // a read-only method
   // upgrade from optimistic read to read lock
   double distanceFromOrigin() {
     long stamp = sl.tryOptimisticRead();
     try {
       retryHoldingLock: for (;; stamp = sl.readLock()) {
         if (stamp == 0L)
           continue retryHoldingLock;
         // possibly racy reads
         double currentX = x;
         double currentY = y;
         if (!sl.validate(stamp))
           continue retryHoldingLock;
         return Math.hypot(currentX, currentY);
       }
     } finally {
       if (StampedLock.isReadLockStamp(stamp))
         sl.unlockRead(stamp);
     }
   }

   // upgrade from optimistic read to write lock
   void moveIfAtOrigin(double newX, double newY) {
     long stamp = sl.tryOptimisticRead();
     try {
       retryHoldingLock: for (;; stamp = sl.writeLock()) {
         if (stamp == 0L)
           continue retryHoldingLock;
         // possibly racy reads
         double currentX = x;
         double currentY = y;
         if (!sl.validate(stamp))
           continue retryHoldingLock;
         if (currentX != 0.0 || currentY != 0.0)
           break;
         stamp = sl.tryConvertToWriteLock(stamp);
         if (stamp == 0L)
           continue retryHoldingLock;
         // exclusive access
         x = newX;
         y = newY;
         return;
       }
     } finally {
       if (StampedLock.isWriteLockStamp(stamp))
         sl.unlockWrite(stamp);
     }
   }

   // Upgrade read lock to write lock
   void moveIfAtOrigin(double newX, double newY) {
     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);
     }
   }
 }