为什么 Java StampedLock 比 ReentrantReadWriteLock 快

Why Java StampedLock faster than ReentrantReadWriteLock

这个 comparison of StampedLock and other locks show that StampedLock 随着竞争的加剧是最快的。但是,这篇文章和其他各种文章 none 列出了它更快的原因。它似乎使用与其他类型的锁相同的 CAS 语义?谁能解释为什么随着竞争的加剧它最快?
例如,在下面这段代码中,writeLock 不仅会阻塞其他 writeLocks,还会阻塞 readLocks。此时我不关心 optimisticReadLocks 等。只是简单的 writeLock .. 有什么优势,它比 ReentrantLock 更快(而且它甚至没有可重入性)。

    public static void main(String[]args)throws Exception{
    StampedLock lk = new StampedLock();

    new Thread(() -> {
        long stamp = lk.writeLock();
        try {
            out.println("locked.. sleeping");
            TimeUnit.SECONDS.sleep(5);
            lk.unlock(stamp);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }).start();

    new Thread(() -> {
        long stamp = lk.writeLock();
        try {
            out.println("locked.. sleeping");
            TimeUnit.SECONDS.sleep(5);
            lk.unlock(stamp);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }).start();

}

需要说明的是,当竞争加剧时,StampedLock 的读取速度要快得多。写入速度稍快,但不如读取速度快。我会解释为什么。

大多数时候,有了读写锁,写操作就会少得多。然而,尽管如此,每次您在 ReentrantReadWriteLock 上获得 readLock() 时,您都必须增加 reader 计数。这会在所有使用此锁的内核上强制缓存失效。

在竞争激烈的情况下,这可能会导致读取速度显着下降。读取应该很快,当执行 readLock() 时,我们不应该更新变量,这是违反直觉的。

如果相反,我们有印记或者比方说版本呢?每次读取迭代只更新一次。

这对我们的作用是,在争论中,如果只有一个线程更新标记值(比方说在写入之后),所有读取线程都将在想要读取锁时执行缓存命中。这禁止缓存失效并允许锁以比 RRWL 更合适的方式执行。

使用 StampedLock 的模式类似于使用 tryOptimisticRead 时的无锁算法(如 CAS)

  1. 盖章
  2. 读取一个值
  3. 邮票改了吗?
    • 是,重试或发出阻塞读取
    • 不,我们很好,我们继续前进。