AQS第三篇 —— 深入源码解读

坑外话:从根本上说 ,如果你不懂AQS ,没有深入的理解AQS的设计艺术,那么你就谈不上真的意义上的懂得JUC编程

先了解一部分关于锁的基础知识

Lock接口的实现类,基本都是通过【聚合】了一个【队列同步器】的子类完成线程访问控制的
我们拿ReentrantLock 来看,内部聚合了一个Sync的子类
在这里插入图片描述

ReentrantLock中公平锁 和 非公平锁
在这里插入图片描述
公平锁 和 非公平 的区别 :
在这里插入图片描述

  1. 可以明显看出公平锁与非公平锁的lock()方法唯一的区别就在于公平锁在获取同步状态时多了一个限制条件:
  2. hasQueuedPredecessors()
  3. hasQueuedPredecessors是公平锁加锁时判断等待队列中是否存在有效节点的方法

在这里插入图片描述
我的理解是 :

  1. 在唤醒线程的时候,公平锁采用的策略 是根据同步队列中,按序的唤醒一个线程,让它去获取锁资源;
  2. 在唤醒线程的时候,非公平锁采用的策略 将同步队列中 所有的线程全部唤醒,让他们去争抢锁资源

下面我们通过ReentrantLock 中的lock () 和 unlock() 理解源码,主要看非公平锁的实现

lock ()

看非公平锁的实现

调用lock()方法时候

在这里插入图片描述

在这里插入图片描述
继续追踪源码 acquire()
在这里插入图片描述

一、 tryAcquire()

这个接口的目的是,让第一次抢不到锁的线程,再去尝试一次,采用是 尽量争抢,不要轻易阻塞的思想

追踪源码 发现,里面直接抛出异常
在这里插入图片描述
这里为什么直接抛出异常呢,这里是一种设计模式 叫 模板设计模式的典型运用,直接给你抛出异常限制你子类必须去实现这个钩子方法;
在这里插入图片描述

查看nofairTryAcquire 方法
在这里插入图片描述

二、addWaiter 方法

当tryAcquire 返回false ,取反后 条件继续判断就来到了 addWaiter 方法
这个方法的作用是让结点入队

那么第一种情况,当前队列中,没有元素,则需要初始化队列
注意:双向链表中,第一个节点为虚节点(也叫哨兵节点),其实并不存储任何信息,只是占位。 真正的第一个有数据的节点,是从第二个节点开始的

在这里插入图片描述
接下来我们看看怎么初始化队列
在这里插入图片描述

第二种情况,队列中已经有元素了
在这里插入图片描述
给大家看个图,应该能清晰不少
在这里插入图片描述

三、acquireQueued 方法

在这里插入图片描述

selfInterrupt 方法

让当前线程阻塞
在这里插入图片描述

unlock()

当锁空闲,则由AQS 负责去唤醒线程

查看源码
在这里插入图片描述
查看release

在这里插入图片描述
查看tryRelease
在这里插入图片描述
又是设计模式的思想,我们去查看他的子类具体实现

在这里插入图片描述

继续看里面唤醒方法 unparkSuccessor

在这里插入图片描述
一看本质上就是采用了LockSupport的unpark方法 ,那些处于等待的线程 就被唤醒
在这里插入图片描述

下面记录两个坑人的题哈

一、我相信你应该看过源码了,那么AQS里面有个变量叫State,它的值有几种?

3个状态:没占用是0,占用了是1,大于1是可重入锁

二、如果AB两个线程进来了以后,请问这个总共有多少个Node节点?

答案是3个 ,因为第一个是哨兵结点

猜你喜欢

转载自blog.csdn.net/weixin_45844836/article/details/112524971