【问题标题】:What is StampedLock in Java?Java 中的 StampedLock 是什么?
【发布时间】:2014-09-29 07:03:59
【问题描述】:

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

提前致谢。

【问题讨论】:

  • 什么是普通多线程
  • @SimY4 我刚刚在文档中读到了关于 Stamped locks 的信息。然后我用谷歌搜索了它。但我不清楚我们为什么要使用它。使用 Stamped 锁肯定有一些优势,这就是 Oracle 在 java 8 中引入它的原因。
  • @Keppil 我已经编辑了我的问题。感谢您纠正我。
  • 你不应该仅仅因为它存在就使用每个现有的功能。如果你无法想象一个适合你的用例,不使用它完全没问题……
  • @Holger 我知道并不是必须使用所有功能,但我很好奇在什么情况下我们可以使用它。

标签: java multithreading java-8 locking


【解决方案1】:

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

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

因此,如果您有一个存在争用的场景(否则您不妨使用synchronized 或简单的Lock)并且读者多于作者,使用 StampedLock 可以显着提高性能。

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

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

【讨论】:

  • 谢谢,现在我知道了我可以使用它的情况并对此有了清晰的认识。
  • @Mohit,点击链接可以更好地理解 StampedLock:dzone.com/articles/a-look-at-stampedlock
  • Heinz 的文章用一个简短的例子完美地解释了所有的同步机制。谢谢!
【解决方案2】:

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

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

例如-

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

【讨论】:

    【解决方案3】:

    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 块中解锁。请记住,标记锁不实现可重入特性。如果没有可用的锁,即使同一个线程已经持有锁,每次对 lock 的调用都会返回一个新的标记并阻塞。所以你要特别注意不要陷入死锁。

    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) 来检查戳记是否有效。

    【讨论】:

    【解决方案4】:

    除了@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);
         }
       }
     }
    

    【讨论】:

      猜你喜欢
      • 2018-02-07
      • 2015-05-09
      • 1970-01-01
      • 1970-01-01
      • 2011-02-12
      • 2011-07-19
      • 2016-09-02
      • 2010-12-10
      • 1970-01-01
      相关资源
      最近更新 更多