栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

我一个面试官都惊了:你居然认为ReentrantLock是轻量级锁?

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

我一个面试官都惊了:你居然认为ReentrantLock是轻量级锁?

目录

前言AQS流程处理

加锁关键点解锁关键点 LockSupport原理给面试官的答案

前言

大家好,我是予学~

前几天面试了好几位小伙伴,在问到关于锁这一块的知识点时,大部分人都会有意无意的提到一个点:ReentrantLock是轻量级锁,相比于synchronized实现更加简单(或者说比synchronized轻量级)。

对于这样的说法,我通常会继续追问以下几个问题:

    你是从什么角度来判断ReentrantLock是轻量级锁的?那如果某个线程CAS失败了,这个线程会怎么处理?既然是通过LockSupport进行阻塞的,那你知道LockSupport原理吗?既然LockSupport也是通过mutex,那为什么还说ReentrantLock是轻量级锁?

这几个问题能接上来的不多,如果你能全部答上来,那么说明你对这一块比较清晰,在面试中一般可以乱杀。

AQS流程处理

我们先回答前面两个问题,不过回答这两个问题之前,我们先大概梳理一下AQS对加锁和解锁的关键点。

加锁关键点

不说代码流程中的细节,我们就第二个问题来分析,加锁时如果CAS失败了会怎么样呢?

观察这段代码,如果尝试CAS失败之后,会调用parkAndCheckInterrupt方法,点进去可以看到,CAS失败后,会封装成一个Node类型的对象加入CLH队列中,然后调用LockSupport.park(this)进行阻塞:

private final boolean parkAndCheckInterrupt() {
    LockSupport.park(this);
    return Thread.interrupted();
}

到这里我们先简单得出一个结论:CAS失败后会通过LockSupport.park(this)对当前线程进行阻塞

解锁关键点

既然已经知道加锁流程中是通过LockSupport进行阻塞的,那么解锁流程不用想,肯定也在某个地方进行了unpark,先看看解锁的模板方法:

在unparkSuccessor方法中,的确是有调用unpark方法进行解锁:

private void unparkSuccessor(Node node) {
    int ws = node.waitStatus;
    if (ws < 0)
        compareAndSetWaitStatus(node, ws, 0);
    Node s = node.next;
    if (s == null || s.waitStatus > 0) {
        s = null;
        for (Node t = tail; t != null && t != node; t = t.prev)
            if (t.waitStatus <= 0)
                s = t;
    }
    if (s != null)
        LockSupport.unpark(s.thread);
}
LockSupport原理

从上面可以知道,AQS虽然有用CAS进行尝试加锁,但是加锁失败后还是通过LockSupport来实现阻塞和解锁的。

那么LockSupport又是通过什么实现阻塞的呢?可惜我们点进去源码会发现它是一个native方法,通过观察源码我们可以发现,实际上阻塞park和唤醒unpark是用到了mutex和condition的方法调用

LockSupport中的park与unpark原理
JUC—LOCKSUPPORT以及PARK、UNPARK方法底层源码深度解析

感兴趣的同学可以去阅读一下这两篇文章,大佬级别。到这里我们又得出来一个结论:
LockSupport也是基于mutex实现的

要知道,synchronized的重量级锁底层依赖的是mutex lock,会有用户态和内核态的切换,才会有AQS的一堆优化,然而我们会发现,AQS也是通过mutex来实现的!

给面试官的答案

到这里我们好像可以给面试官满意的答案:

实际上ReentrantLock在CAS加锁失败之后会封装成一个Node类型的对象加入CLH队列中,然后调用LockSupport.park(this)进行阻塞
而LockSupport是一个native方法实现的工具类,在hotspot源码中也是通过mutex来实现的,一些情况下它的开销可能不会比Synchronized好

可惜我还是会接着问你最后一个问题:
那么ReentrantLock和Synchronized我们该如何选择呢?

这里我也给一份比较好的答案(我抄来的~):

Lock是通过自旋CAS和Unsafe.park/unpark挂起唤醒线程来实现的,而synchronized在jdk1.6后重量级锁也是通过CAS自旋以及park/unpark来实现的,都有进行用户态和内核态的切换。


但是synchronized做了优化后在前面的偏向锁拿到锁的线程不会进行CAS自旋,而轻量级锁也只是进行CAS自旋不会阻塞挂起,只有膨胀到重量级锁后才会自旋CAS+park/unpark来挂起唤醒线程!

所以理论上synchronized的效率应该比Lock快一点点,但是Lock提供的API比较简单方便!

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/776227.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号