zoukankan      html  css  js  c++  java
  • 除了读写锁,JUC 下面还有个 StampedLock!还不过来了解一下么?

    前言

    在了解完 ReentrantLock 和 ReentrantReadWriteLock 之后,惊奇的发现 JUC 下还有一个 StampedLock 。 查阅资料发现是 JDK8 新增的一个锁。现在已经 JDK15 了,原谅我的孤陋寡闻,实在是业务开发中用的太少。那行吧,赶紧来看一下 StampedLock 到底是什么?为什么有了 ReentrantLock 和 ReentrantReadWriteLock 之后还要设计一个 StampedLock ?

    介绍

    往期回顾

    在介绍 StampedLock 之前还是先看一下 ReentrantLock 和 ReentrantReadWriteLock。

    ReentrantLock:互斥锁,同时只有一个线程可以持有。支持锁重入。

    ReentrantReadWriteLock:读写锁,分为读锁和写锁,支持重入。其中读读共享,写写独占,读写互斥,写读互斥。支持锁降级,线程获取写锁后可以降级为读锁。适合读多写少的场景。

    那为什么要设计 StampedLock 呢?先来看一下源码上的注释:

    StampedLock

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

    三种模式是:写锁、读锁、乐观读锁。

    并且具有以下特征:

    1. 获取锁的时候(无论读锁还是写锁或者乐观读锁)都会返回一个 stamp,在释放锁的时候需要使用这个 stamp;
    2. 支持三种模式转换;
    3. 不是可重入的,所以获得锁的时候,不要尝试再次获取。

    使用样例

    /**
     * @author liuzhihang
     * @date 2020/8/6 15:27
     */
    public class Count {
    
        private int num;
        private final StampedLock stampedLock = new StampedLock();
    
        // 使用写锁,在对 num 进行写入的时候加锁
        void write(int x) {
            long stamp = stampedLock.writeLock();
            try {
                num += x;
            } finally {
                stampedLock.unlockWrite(stamp);
            }
        }
    
        // 乐观读
        int read() {
            // 获取乐观读锁;返回 stamp
            long stamp = stampedLock.tryOptimisticRead();
            try {
                // validate 验证是否被写锁持有
                // 没有被写锁持有,可以直接返回
                if (!stampedLock.validate(stamp)) {
                    // 被写锁持有,那只能获取读锁
                    stamp = stampedLock.readLock();
                }
                return num;
            } finally {
                stampedLock.unlockRead(stamp);
            }
        }
    
        void convertWrite(int x) {
            // 读
            long stamp = stampedLock.readLock();
            try {
                while (num == x) {
                    // 满足条件,转换为写锁
                    long ws = stampedLock.tryConvertToWriteLock(stamp);
                    // 转为写锁成功
                    if (ws != 0L) {
                        stamp = ws;
                        num = x;
                        break;
                    } else {
                        // 转换失败,释放读锁
                        stampedLock.unlockRead(stamp);
                        // 再次获取写锁
                        stamp = stampedLock.writeLock();
                    }
                }
            } finally {
                stampedLock.unlock(stamp);
            }
        }
    }
    
    1. 写锁使用方法一样;
    2. 乐观读,可以先去读数据,发现没有改变可以返回,发现改变了,则重新获取读锁,然后再返回;
    3. 读锁可以升级为写锁,通过 tryConvertToWriteLock 方法。

    总结

    UML

    通过 UML 可以看出 StampedLock 和 AQS 并无任何关系。

    StampedLock 和 ReentrantReadWriteLock 的区别?

    1. StampedLock 也是读写锁,但是和 AQS 没有关系
    2. StampedLock 除了 读锁和写锁,还有一个乐观读。
    3. StampedLock 的读锁可以升级为写锁。
    4. StampedLock 不支持锁重入。

    小结

    本文主要介绍 StampedLock 的相关使用及和 ReentrantReadWriteLock 的区别。

    因为工作确实很少使用,阅读源码,内部自旋逻辑等有很多。如果介绍的话会篇幅特别长,这里就省略了。有兴趣的小伙伴可以自己阅读源码。

    因为 StampedLock 提供的乐观读锁支持,所以在多线程多读情况下,性能比 ReentrantReadWriteLock 要更好,但是需要注意的是 StampedLock 是不支持锁重入的。

    另一个需要记住的就是 StampedLock 和 AQS 并没有什么关系,它是在自己内部维护了一个双向阻塞队列。

  • 相关阅读:
    定时任务框架APScheduler学习详解
    Python list 和 str 互转
    python多个变量赋值
    python数据类型转换
    jquery datetimepicker 配置参数
    c#判断是否为合法的email地址
    SQL的四种连接-左外连接、右外连接、内连接、全连接
    SQL表连接查询(inner join、full join、left join、right join)
    简单的日期转换
    递归
  • 原文地址:https://www.cnblogs.com/liuzhihang/p/StampedLock.html
Copyright © 2011-2022 走看看