高并发中StampedLock理解与应用

ReadWriteLock大家都用过或者听说过,可以解决多线程同时读,但只有一个线程能写的问题。如果我们深入分析ReadWriteLock,会发现它有个潜在的问题:如果有线程正在读,写线程需要等待读线程释放锁后才能获取写锁,即读的过程中不允许写,这是一种悲观的读锁。
要进一步提升并发的效率,Java 8引入了新的读写锁:StampedLock

一.StampedLock 简介

基于功能的锁,具有三种模式来控制读/写访问。StampedLock的状态由版本和模式组成。锁获取方法返回一个表示并控制相对于锁状态的访问的戳记;这些方法的“尝试”版本可以改为返回特殊值零,以表示获取访问失败。锁释放和转换方法需要使用图章作为参数,如果它们与锁的状态不匹配,则会失败。三种模式是:

  • 写作:方法writeLock()可能会阻止等待独占访问,并返回一个可用于方法unlockWrite(long)中释放锁的标记。tryWriteLock还提供了未定时和定时版本。当锁保持在写模式时,可能不会获得任何读锁,并且所有乐观读验证都将失败。
  • 悲观读锁:方法readLock()可能会阻止等待非排他性访问,并返回可以在方法中使用unlockRead(long)以释放锁的标记。tryReadLock还提供了未定时和定时版本。
  • 乐观的读:tryOptimisticRead() 仅当锁当前未处于写入模式时,该方法才返回非零戳。方法validate(long)如果自获取给定标记以来未在写模式下获取锁,则返回true。可以将这种模式视为读取锁的极弱版本,编写者可以随时将其破坏。在短的只读代码段中使用乐观模式通常可以减少争用并提高吞吐量。然而,其使用固有地易碎。乐观的读取部分应仅读取字段并将其保存在局部变量中,以供验证后使用。在乐观模式下读取的字段可能完全不一致,因此用法仅在您足够熟悉数据表示以检查一致性和/或重复调用方法时适用validate()。例如,当首先读取对象或数组引用,然后访问其字段,元素或方法之一时,通常需要执行这些步骤。

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

二.区别

ReadWriteLock 支持两种模式:一种是读锁,一种是写锁。
StampedLock 支持三种模式,分别是:写锁、悲观读锁和乐观读。其中,写锁、悲观读锁的语义和 ReadWriteLock 的写锁、读锁的语义非常类似,允许多个线程同时获取悲观读锁,但是只允许一个线程获取写锁,写锁和悲观读锁是互斥的。不同的是:StampedLock 里的写锁和悲观读锁加锁成功之后,都会返回一个 stamp;然后解锁的时候,需要传入这个 stamp。
StampedLock提供了乐观读锁,可取代ReadWriteLock以进一步提升并发性能;StampedLock是不可重入锁,ReadWriteLock可重入。

三.常用Api

在这里插入图片描述
在这里插入图片描述

四.代码示例

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

    /**
     * 获取写锁
     * @param deltaX
     * @param deltaY
     */
    void move(double deltaX, double deltaY) {
        long stamp = sl.writeLock();
        try {
            x += deltaX;
            y += deltaY;
        } finally {
            sl.unlockWrite(stamp);
        }
    }

    /**
     *  获取读锁
     * @return
     */
    double distanceFromOrigin() { 
        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);
    }

    /**
     * 获取乐观读,进行锁的升级
     * @param newX
     * @param newY
     */
    void moveIfAtOrigin(double newX, double newY) { // 
        // 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);
        }
    }
}

本文的分享暂时就到这里,希望对您有所帮助
关注 Java有货领取更多资料

联系小编。微信:372787553,带您进群互相学习
左侧小编微信,右侧获取免费资料
在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/weixin_38937840/article/details/105032947