我惊了:你居然认为ReentrantLock是轻量级锁?

6 篇文章 2 订阅
2 篇文章 1 订阅

前言

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

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

  1. 你是从什么角度来判断ReentrantLock是轻量级锁的?
  2. 那如果某个线程CAS失败了,这个线程会怎么处理?
  3. 既然是通过LockSupport进行阻塞的,那你知道LockSupport原理吗?
  4. 既然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比较简单方便!

  • 4
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 5
    评论
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值