• JDK17 ReentrantLock 简述 lock()、unLock()


    背景

    • 最近无聊翻一翻 juc ,想看 AQS 入队的是怎么入队的,由于代码量比较大,就想先看了 ReentrantLock 入队操作,然后举一反三,由于我看的是 JDK 17 的版本,与 JDK 8 可能不一样,先不去考证 JDK 8 内容,只看 JDK 17 的代码,并且只讨论 ReentrantLock 中的方法

    读代码

    lock()

    • 首先肯定要读 lock() 和 unLock() 两个方法,实际写代码就是用这两个完成重入锁操作
    	public void lock() {
            sync.lock();
        }
    
    • 1
    • 2
    • 3

    肯定走这个方法了吧,当我 ctrl + 鼠标左键调到具体方法时,发现并没有这样显示:
    在这里插入图片描述
    而是直接跳到了

    		@ReservedStackAccess
            final void lock() {
                if (!initialTryLock())
                    acquire(1);
            }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    这个 initialTryLock() 是一个抽象方法,而 acquire(1) 调用的是 AQS 类的方法,AQS 的类代码先不讨论,反正不管公平还是非公平锁最后都调用 acquire(),也就是入队 的简称 ;
    先看 ReentrantLock 的内容,当我点击 initalTryLock() 方法时出现了,公平锁和非公平锁两个选项:
    在这里插入图片描述
    现在比较一下两个不同的 initialTryLock() 实现:
    在 idea 中打开两个相同的类
    在这里插入图片描述
    两部分代码其实只有红色的部分不一样:
    在这里插入图片描述
    相比较之下只是公平锁多出了:

    			int c = getState();
                if (c == 0) {
                    if (!hasQueuedThreads()
    
    • 1
    • 2
    • 3

    很好理解,先获取 AQS 中 state 变量的属性,如果属性是 0,再去看线程队列中有没有人排队,没人排队再去竞争下,很显然公平锁还是讲武德的,别人在排队了,我就不去竞争了;相比较,非公平锁,来了就去竞争下,没竞争到再入队,这年轻人xxxx;

    这里会有疑问,什么是 state 属性,什么是队列呢,说说我自己的理解?

    • 要从 AQS 设计说起,AQS 本身就是一个普通抽象类,与我们平时自己写的抽象类一样,里面有一个用 volatile 关键字修饰 int 类型的成员变量 state ,因为有 volatile 关键字修饰,那么多个线程都能看见这个 state ,如果多个线程竞争同一个资源时,都去参考 state 的值,这个 AQS 不就相对于一把锁了吗,当 state=0 时,代表没人竞争,代码继续执行,当 state > 0 时,代表有人持有这个资源,就去等待,等持有锁的线程执行完再去竞争资源,执行这个锁与 synchronized 相比哪个性能好先不去讨论,先看 lock 时引出的两个问题;

    • 一个是 state 是值从 0 变为大于 0 ,怎么保证原子性,另一个是当 state > 0,等待的线程去哪里等待或者叫记录他们线程信息 和 先来后到顺序;第一个问题有 cas 算法呀,第二个使用 node 队列解决, node 有一个属性 waiter 记录线程信息,队列来保证先来后到顺序;这不就行了吗

    • 当 state >1 代表什么呢,在 ReentrantLock 中代表重入次数,在 CountDownLatch 中代表倒计次数开门闩,其他的 juc 类没怎么看,就不说了;

    unLock()

    unLock() 代码 ReentrantLock 的公平锁和非公平锁调用的是同一套释放锁方法:

    	public void unlock() {
            sync.release(1);
        }
        public final boolean release(int arg) {
            if (tryRelease(arg)) {
                signalNext(head);
                return true;
            }
            return false;
        }
        @ReservedStackAccess
        protected final boolean tryRelease(int releases) {
            int c = getState() - releases;
            if (getExclusiveOwnerThread() != Thread.currentThread())
                throw new IllegalMonitorStateException();
            boolean free = (c == 0);
            if (free)
                setExclusiveOwnerThread(null);
            setState(c);
            return free;
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    这里就不展开说了,写的有点长了, tryRelease(arg) 就是 tryRelease(1),tryRelease(1) 就是把 AQS 中的 state 属性减 1,当减到 0 时,返回 ture,通知 AQS 队列头结点的下一个;

    整体架构评价

    • 在看 JDK17 的 ReentrantLock 时我发现代码结构更精简了。以前我以为有两个内部类 FairSync 和 NonFair ,现在有多了一个 Sync 类,把 FairSync 和 NoFair 的共有属性放到了 Sync 类,首先FairSync 和 NonFairSync 都变精简了,其次方便观察实现不同:
      在这里插入图片描述
  • 相关阅读:
    Java8-Java16部分重要新特性汇总
    算法~利用zset实现滑动窗口限流
    【广州华锐互动】VR内容创作工具极大地丰富了现代教学内容和形式
    【解决】如何在JavaScript中终止forEach循环
    60 最长有效括号
    苹果平板可以用别的电容笔吗?电容笔和Apple pencil区别
    mybatis ORDER BY FIND_IN_SET 失效的一次问题排查
    摘要-签名-PKI-访问控制-DOS-欺骗技术
    Ant Design Pro【面包屑导航】二级路由和三级路由都有component的情况,三级不显示component的页面,怎么解决?
    反射课后习题及做题记录
  • 原文地址:https://blog.csdn.net/x123453316/article/details/126595772