为什么Java StampedLock比ReentrantReadWriteLock更快

4
这个StampedLock和其他锁的比较表明,随着争用的增加,StampedLock 是最快的。然而,这篇文章以及其他各种文章都没有列出它为什么更快。它似乎使用了与其他类型的锁相同的CAS语义?有人能解释一下为什么随着争用的增加它是最快的吗?
例如,在下面的代码中,writeLock 不仅阻塞其他 writeLock,还阻塞 readLock。我现在不关心 optimisticReadLock 等。只是普通的 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();

}
1个回答

10
要明确的是,当争用增加时,StampedLock 在读取方面要快得多。写作速度更快,但与读作速度相比,差距不大。我将解释原因。
大多数情况下,使用读写锁时,写入操作要少得多。然而,即使考虑到这一点,每次你在 ReentrantReadWriteLock 上获取 readLock() 时,都必须增加一个 reader count。这会强制所有使用此锁的内核进行缓存失效。
在高度争用的情况下,这可能会导致读取过程显著变慢。读取应该很快,当执行 readLock() 时,我们不应该更新变量,这与直觉不符。
那么,如果我们有一个戳记或版本呢?这个版本仅在每次读取迭代中更新一次。
对于我们来说,这样做是什么意思呢?在争用情况下,如果只有一个线程更新戳记值(例如,在写入后),所有读取线程在想要在锁上读取时都将执行缓存命中。这会禁止缓存失效,并允许锁以比 RRWL 更适当的方式执行。
使用 StampedLock 的模式类似于使用 tryOptimisticRead(如 CAS)的无锁算法。
1. 获取戳记 2. 读取一个值 3. 戳记是否改变?
是:重试或发出阻塞读取请求 否:我们做得很好,继续前进。

嗨John,甚至StampedLock似乎在readLock中使用了一些写入操作..看看这个>> U.compareAndSwapLong(this, STATE, s, next = s + RUNIT) - Apurva Singh
约翰,也许唯一的优化就是尝试乐观读取..这是在步骤1、2、3中以你上述方式使用的。读锁和写锁似乎很普通。 - Apurva Singh
没错,@ApurvaSingh,tryOptimisticRead 在这里真的非常有用。 - John Vint
我意识到我漏掉了那部分,我编辑了我的答案,谢谢! - John Vint

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接