Java精通并发-可重入锁对于AQS的实现源码分析

在上一次Java精通并发-AQS整体架构与设计原则剖析对于AQS进行了一个整体的认识,这次则通过一个具体的例子来进一步来了解AQS。

从简单的示例开始:

如标题所示,这里会用到可重入锁,而ReentrantLock就可以支持可重入锁,关于它的使用可以参考https://www.cnblogs.com/webor2006/p/11792954.html,这里再来重温一下它的使用,比较简单,但是简单的使用其背后是不简单的,而咱们重温它的目的而是剖析它的背后,直接贴出来:

package com.javacurrency.test8;

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.stream.IntStream;

public class MyTest1 {
    private Lock lock = new ReentrantLock();

    public void method() {
        try {
            lock.lock();

            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            System.out.println("method");

        } finally {
            lock.unlock();
        }
    }

    public static void main(String[] args) {
        MyTest1 myTest1 = new MyTest1();
        IntStream.range(0, 10).forEach(i -> {
            new Thread(() -> {
                myTest1.method();
            }).start();
        });
    }
}

运行:

程序很顺其自然,接下来则以它为入口,看它对于进一步了解AQS这个东东有何贡献?

探究它对于AQS的源码分析:

ReentrantLock跟AQS的关系:

既然要通过ReentrantLock来进一步了解AQS,那它跟AQS何关系呢,其实在上一次已经初步看过了,这里再来瞅一下:

而AbstractQueuedSynchronizer就是AQS,而在上一次学习也揭示了其具体AQS使用的套路基本都是定义了一个Sync内部类,然后具体由它来实现AQS的细节。

AQS抽象类核心抽象方法纵览:

在进行具体ReentrantLocak关于AQS的具体实现逻辑分析之前,先来对AQS这个类再进行一个简单了解,我们知道它是一个抽象类:

然后它里面定义了若干个protected的重要核心方法,而这些受保护的方法它具体逻辑的执行应该取决于它的子类它所处理的具体业务的场景以及子类所代表的具体的业务的含义,特别是对于它里面的state这个字段:

到底是共享锁,还是排它锁,其state的含义表示的也是不一样的,下面粗略地来看一下它里面定义protected的方法,为之后具体这些方法的实现源码分析做一个准备:

tryAcquire():

里面的实现直接抛出异常了,很明显就是子类是一定要来实现它的,先来大致看一下它的介绍:

其中“exclusive mode”指的是排它锁,也就是如果我获取了这把锁其它人休想获取~~

tryRelease():

tryAcquireShared():

看一下它的介绍:

而共享模式就是我获取了锁后,他人也可以获取,一起愉快的玩耍~~

tryReleaseShared():

isHeldExclusively():

言外之意就是说此方法就是用来判断锁的类型是排它锁还是共享锁,如果为true则表示此锁是排它锁,否则为共享锁。

总结:

对于上述提到的方法,其逻辑应该是这样:

如果判断具体AQS的isHeldExclusively()=true,那么子类则需要选择性地实现tryAcquire()、tryRelease()方法;而如果为false,则子类需要选择性地实现tryAcquireShared()、tryReleaseShared()方法了。

ReentrantLock关于AQS实现源码剖析:

纵览一下Sync内部类实现:

在上面分析AQS核心方法中不是一定会有一个成对的tryAcquire()、tryRelease()方法需要具体子类来实现么,不管是共享模式还是排它模式,下面下咱们来大致瞅一下对于ReentrantLocak类中的具体AQS的实现子类有木有实现:

这里只实现了AQS中的两个方法,很明显对于ReentrantLock而言它是一个独占式的锁,所以并没有去实现tryAcquireShared()、tryReleaseShared()这俩方法对吧?但是!!!对于之前咱们所总结的:

这里只定义了tryRelease()锁释放的方法,木有看到tryAcquire()锁获取的方法的呀,为啥呢?继续往下看。

寻找tryAcquire()----公平锁与非公平锁产生:

这是因为对于ReentrantLock而言它里面有两种锁类型:公平锁(其含义跟synchronized类似,就是说按照顺序进行锁的获取,不存在插队的情况,很公平)与非公平锁(如果获取到了锁,则直接插队进行执行,不用管先来后到的顺序,当然如果获取不到还是得老老实实地进行排队),但是很奇怪我们在使用时木有指定锁类型的呀,看一下目前的代码:

其它答案在源码中,进去看一下构造:

上图中提到了一个“默认”二字,很显然还存在自定义嘛,是的,有另一个版本的构造重载:

关于这俩锁的实现细节之后再进行分析,这里重点来搜寻tryAcquire()实现的位置,其实到这已经非常明显了,看一下在构造时都将实例直接赋值给了sync变量了:

而Sync类不就是我们已经看过了么,在它里面没有找到tryAcquire()方法,由于Sync类也是一个抽象类:

抽象类中没有找到方法,是不是可以往它的子类找呀,嗯,很简单明了的原则,很明显它的子类就是FairSync和NonfairSync了,此时就可以找到我们想要看的这个方法啦:

到这里就可以完全根据咱们在抽象AQS梳理的总结中所分析的吻合了~~

lock():

接下来按照调用的逻辑来进行分析,首先会从这里开始:

此时就会调用这:

而我们知道此sync有两个版本:

下面则分别进行一下剖析。

FairSync.lock():

此时调到了AQS来了:

分析一下它里面的逻辑:

此时这个方法就熟悉了吧,在前面分析AQS中核心抽象方法已经介绍过了,它是用来尝试在排它模式下进行锁的获取的,实现是需要由子类来实现的,因为它里面是抽象的:

此时回到公平锁类中来,看在公平的情况下是如何来获取锁的:

对于在排它模式下,此时的state的含义就代表已经持有锁的线程的个数,【注意:分析sate一定是要在某种场景下分析,不同的场景其它代码的含义也是不一样的,如这句话中的“在排它模式下”】,继续往下:

进入条件里面看一下:

又调到AQS上来了,那这方法是干嘛的呢,再往里跟一下:

呃,jni调用,好吧,先回来,不分析这么深了,目前看不到细节则回到此方法了解一下它的说明吧:

好,解决了一个条件了,接下来再来解决另一个条件:

这是干嘛的呢?其实是判断AQS的队列中有木有等待的线程,还记得在Java精通并发-AQS整体架构与设计原则剖析AQS整体介绍时提到了一个队列的概念么:

此判断就是判断它的,这里细节就不细看了,回到主流程上来:

所以此时就可以进入条件里面了:

而在返回之前执行了设置一下排它的线程为当前线程,那看一下它的实现:

好,回到主流程看另一个分支:

也就是当前的获取锁的线程数不止一个时,而其中当前的线程就是之前成功获取锁的排它线程时,则会执行此条件了。这里反问一下:对于一个线程难道可以获取多次锁呀。。是的,因为ReentrantLock是支持重入锁的,所谓重入锁也就是同一个线程可以多次获取锁:比如:

,每获取一次则AQS的state都会+1,所以看一下这块的条件比较简单:

这就是整个关于公平锁上锁的逻辑,还是比较好理解的。

NonfairSync.lock():

接下来分析一下非公平锁上锁的逻辑:

看到木有,这就是区别,对于非公平锁不用考虑AQS的队列,获得到了锁就直接开始执行了,那如果锁没获取到则会执行else条件了,如下:

此时又会调用AQS的方法了:

此时它的实现在非公平类中,所以看一下:

此时会调用Sync抽象类来:

其逻辑看起来貌似跟公平锁的差不多的样子:

当然是存在差异的,下面来分析一下非公平的这个逻辑:

注意!!!这里就看出跟公平锁的区别啦,少了一个条件了:

刚好跟非公平的场景吻合嘛,我只要获取到了锁就开始插队执行了,不用排队~~接着再分析esle的条件,此时就跟公平锁是一模一样的:

因为对于公平锁与非公平锁其实都是可以重入的。最后再来回到AQS中的acquire()中看剩下的逻辑:

这里在调用addWaiter时传的是:

这里其实涉及到Node的几个重要状态了,了解一下:

然后上图中看到还有几个常量值,也是很重要的状态值,也来了解一下:

总结:

经过上面的分析对于ReentrantLock的公平锁与非公平锁整个的获取锁的逻辑跟synchronized在c++层面的逻辑几乎是一模一样的(关于这块可以参考https://www.cnblogs.com/webor2006/p/11442551.html,另外对于Lock与synchronized的区别可以参考https://www.cnblogs.com/webor2006/p/11792954.html),也就是synchronized掌握好了,对于理解AQS的学习是非常有帮助的。

关注个人公众号,获得实时推送

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

webor2006

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值