Java多线程编程- StampedLock (JDK8新增)

Java多线程编程- StampedLock


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

Writing模式

方法writeLock可能会阻止等待独占访问,并返回一个stamp,该stamp可以在方法 unlockWrite中使用以释放锁。还提供了tryWriteLock的非定时版本和定时版本。当锁保持在写模式时,将无法获得任何读锁,并且所有乐观的读验证都将失败。

Reading模式

方法readLock可能会阻止等待非独占访问,返回可以在方法unlockRead中使用的stamp,以释放锁。还提供了tryReadLock的非定时版本和定时版本。Reading是一个共享锁,在没有线程获取独占写锁的情况下,多个线程可以同时获取该锁。如果己经有线程持有写锁,则其他线程请求获取该读锁会被阻塞,这类似于 ReentrantReadWriteLock 的读锁 (不同的是这里的读锁是不可重入锁〉。这里说的悲观是指在具体操作数据前其会悲观地认为其他线程可能要对自己操作的数据进行修改,所以需要先对数据加锁,这是在读少写多的情况下的一种考虑 。请求该锁成功后会返回 一个 stamp 变量用来表示该锁的版本,当释放该锁时需要调用unlockRead 方法并传递 stamp 参数。并且它提供了非阻塞的 tryReadLock 方法 。

Optimistic Reading模式

它是相对于悲观锁来说的,在操作数据前并没有通过CAS 设置锁的状态,仅仅通过位运算测试。如果当前没有线程持有写锁 ,则 简单地返回 一个非 0 的 stamp 版本信息 。 获取该 stamp 后在具体操作数据前还需要调用validate 方法验证该 s tamp 是否己经不可用,也就是看当调用 trγOptimisticRead 返回stamp 后到 当前 时间期间是 否有其他线程持有了写锁,如果是则 va lidate 会返回 o ,否则就可以使用该 stamp 版本的锁对数据进行操作 。 由于 tryOptimisticRead 并没有
使用 CAS 设置锁状态,所以不需要显式地释放该锁 。 该锁的一个特点是适用于读多写少的场景 , 因为获取读锁只是使用位操作进行检验,不涉及 CAS 操作,所以效率会高很多,但是同时由于没有使用真正的锁,在保证数据一致性上需要复制一份要操作的变量到方法钱,并且在操作数据时可能其他写线程己经修改了数据,而我们操作的是方法战里面的数据,也就是一个快照,所以最多返回 的不是最新的数据,但是一致性还是得到保障的 。
StampedLock 还 支持这三种锁在一定条件下进行相互转换:
1.当前锁己经是写锁模式了 。
2.当前锁处于读锁模式, 并且没有其他线程是读锁模式
3.当 前处于乐观读模式,井且当前写锁可用 。
我们就用官网提供的案例进行分析:

public class Point {
    //成员变量
    private double x, y;

    //锁实例
    private final StampedLock sl = new StampedLock();

    //排他锁
    void move(double deltaX, double deltaY) { // 独占锁定方法
        System.out.println("current thread name:" + Thread.currentThread().getName());
        //创建写锁
        long stamp = sl.writeLock();
        try {
            x += deltaX;
            y += deltaY;
        } finally {
            //释放写锁
            sl.unlockWrite(stamp);
        }
    }

    //乐观读锁
    double distanceFromOrigin() {
        //尝试获取乐观读锁
        long stamp = sl.tryOptimisticRead();
        //将全部交量复制到方法体校内
        double currentX = x, currentY = y;
        //验证读锁的stamp 锁有没被其他写线程排它性抢占
        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) {
        // 这里可以使用乐观读锁替换
        long stamp = sl.readLock();
        try {
            //如采当前点在原点则移动
            while (x == 0.0 && y == 0.0) {
                //尝试将获取的读锁升级为写锁
                long ws = sl.tryConvertToWriteLock(stamp);
                //升级成功 ,则更新stamp,并设置坐标值 , 然后退出循环
                if (ws != 0L) {
                    stamp = ws;
                    x = newX;
                    y = newY;
                    break;
                } else {
                    //读锁升级写锁失败则释放读锁,显式获取独 占写锁, 然后循环重试
                    sl.unlockRead(stamp);
                    stamp = sl.writeLock();
                }
            }
        } finally {
            //释放锁
            sl.unlock(stamp);
        }
    }
}

StampedLock 的读写锁都是不可重入锁,所以在获取锁后释放锁前不应该再调用会获取锁的操作,以避免造成调用线程被阻塞。当多个线程同时尝试获取读锁和写锁时,谁先获取锁没有一定的规则,完全都是尽力而为,是随机的 。并且该锁不是直接实现Lock 或 ReadWriteLock 接口 ,而是其在 内部自己维护了 一个双 向阻塞 队列

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值