JAVA并发编程ReentrantLock核心原理剖析

2024-09-12 16:09:15 浏览数 (3)

JAVA并发编程系列以及陆续出了5篇,第六篇的主角ReentrantLock该出场了。之前《一文看懂全部锁机制》谈到可重入锁、《JAVA并发编程AQS原理剖析》谈到了JUC灵魂AQS,那么AQS的思想优秀实践者ReentrantLock是怎么实现AQS的呢?

1、ReentrantLock是什么,有哪些优点

ReentrantLock英文翻译以及顾名思义:可重入锁。之前文章说过,还有synchronized也是可重入锁。竟然JDK最开始有了synchronized这个可重入锁,而且JDK1.6之后也对synchronized进行锁优化,性能堪比JUC里的其他Lock()。为什么还提供了ReentrantLock呢?在《JAVA并发编程volatile核心原理》文中开头我们就简单的列了synchronized的几个缺点,包括:阻塞时间过长,不可中断、是非公平锁。

所以,同样是可重入锁,ReentrantLock必须有一些亮点。它的优点就是较好的补足synchronized的缺点,提供了非常灵活多变的特性,满足系统项目研发的需求。

优点有:

1、支持公平锁 非公平锁。(synchronized仅支持非公平锁)

2、支持响应中断、超时。(synchronized不支持超时)

3、支持线程尝试获取锁。(synchronized不支持)

2、具体说说ReentrantLock的原理,看过ReentrantLock源码吗

话说到这份上,好像不看源码不行了。上源码,不要慌,它的核心源码就这几行,先扫一眼:

刚面试官问:具体说说它的原理?

少废话,一句话总结它的原理:ReentrantLock是AQS的具体实现,实现了公平锁和不公平锁。ReentrantLock源码里有三个内部类。

一个是Sync抽象类;sync 继承了AQS 队列同步器。所以ReentrantLock是AQS的实现。

一个是FailSync类,实现了Sync类,实现的是公平锁。

一个是NonFairSync类,也是实现Sync类,实现的是非公平锁。

“好家伙,总结的还不错”面试官心嘀咕着,继续问:

2.1 公平锁是怎么实现的?

我们直接看源码,公平锁的lock加锁是acquire(1)方法。

接下来源码是,就看到这里就很清晰了。

代码语言:txt复制
    public final void acquire(int arg) {
        if (!tryAcquire(arg) &&
            acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
            selfInterrupt();
    }

首先,公平锁加锁可能有三部逻辑执行。

第一步:tryAcquire(1),尝试去获取锁。如果成功了,就没有后面两个步骤的事。

第二步:如果第一步获取失败,把当前线程封装为独占锁类型NODE 进入AQS 的FIFO 队列,等待被按顺序唤醒。

第三步:第二步执行后,也会调用selfInterrupt(),里面的源码就是线程自我中断,修改中断标识位为:true。

那具体再说说第一步tryAcquire(1),是如何获取线程锁的?(候选人内心有点破防:面试官问题咋那么多...)

具体就是:

1、先判断当前AQS队列的state是否为0,如果是0,说明现在可以竞争锁。

2、接下来继续判断AQS的同步队列,如果队列是空、或者队列不为空且队列里的节点是不是自己,就去竞争锁。

3、如果竞争锁成功,通过CAS去设置AQS的state值为1,并帮AQS的当前占有锁的NODE 引用线程改完自己。

源码如下:

2.2 非公平锁又是怎么实现的?

new ReentrantLock()默认的就是非公平锁。

代码语言:txt复制
    public ReentrantLock() {
        sync = new NonfairSync();
    }

非公平锁的竞争锁就比较暴力,上来就直接通过CAS去竞争,

如果获取失败,进入acquire(1),继续去尝试获取锁。

这里和公平锁也是不一样,它没有判断队列情况,继续用CAS方式去看能否修改state获取锁。

如果竞争失败,就会进入AQS队列,等待被唤醒....

今天暂时分享到这,其实可重入锁里面的源码还有很多内容,篇幅有限,等源码专区出来再全部分享。

0 人点赞