首页 > 图灵资讯 > 技术篇>正文

浅析ReentrantLock和AQS

2023-04-19 16:05:50

AQS的全称是AbstractQuedSynchronizer,是AQS框架的核心抽象类。Reentrantlock有三个内部类:Sync、NonfairSync、FairSync。Fairsync代表公平锁,Nonfairsync代表非公平锁,Nonfairsync和Fairsync都继承自Sync,Sync继承自AbstractQuedsynchronizer。

来自Object的AQSwait/notify,因此,首先要了解object的wait//notify。

一、synchronized-wait-notify机制

wait():当前线程首先要用synchronized获取object或class的监控锁(以下简称锁),成功获取锁后才能调用wait()。wait()此时至少要做三件事:将当前线程放入等待锁的集合,释放锁,当前线程进入阻塞状态。接到通知后,线程被唤醒,wait()继续执行,wait() 再次尝试获得锁,获得锁后才能成功退出wait() 。

notify():在实施notify()方法之前,当前线程必须先获得锁,成功获得锁,notify()方法从等待锁的集合中随机选择线程唤醒。只有当当前线程释放锁时,才能真正实现唤醒。

wait()和notify()必须使用相同的锁。

以上两个集合:等待锁释放的线程集合,等待被唤醒的线程集合。

AQS框架取代了synchronized/wait/notify,显示上述两个集合。

二、AQS结构简介

要理解AQS框架,我们必须理解代码逻辑。这里简要介绍一下类似的结构。

AbstractQuedsynchronizer有四个关键实例变量:

① private volatile int state:代表当前线程进入锁的重入数。

② private transient Thread exclusiveOwnerThread:继承Abstractownablesynchronizer,代表当前获得锁的线程。

③ private transient volatile Node head:同步队列的头节点

④ private transient volatile Node tail:尾节点同步队列

Conditionobject是AbstractQuedsynchronizer的内部类别,定义了条件队列和等待通知机制。

Node也是AbstractQuedsynchronizer的内部类别,代表同步队列和条件队列的节点。Node有前驱引用和后驱引用,所以同步队列和条件队列都是双向链表。Node有一个与通知机制有关的重要实例变量waitstatus。

三、Reentrantlock加锁主流程

1、公平锁:第一行直接获得锁,state从0变为1,不构建同步队列。exclusiveownerthread被赋值为第一行引用,state被赋值为1。如果第二个线程第一次锁定失败,将构建同步队列。队列有两个节点。第一个是虚拟节点代表锁定线程,第二个节点包装第二个线程。如果第二次锁失败,第一个线程(前一个队列节点)waitstatus将被放置为-1,这意味着第一个线程释放锁后需要通知并唤醒后续节点,然后进入blocking状态。如果第三个线程锁定失败,new将Node节点包装的第三个线程加入同步队列,将前一个节点的waitstatus放置为-1。

2、非公平锁:无论同步队列如何,都要先尝试直接获得锁。只有当获得失败时,才会加入同步队列。后续过程与公平锁相同。

四、Reentrantlock解锁主流程

公平锁和非公平锁的过程是一样的。它们都是同步队列的第一个节点,并唤醒第二个节点加锁。如果取消第二个节点,则转换为从尾部找到堵塞的节点。

五、Conditionawait()主流程

await()首先判断当前线程是否占用了锁,然后才能继续占用锁。然后new有一个新的Node例子,包装当前线程,waitstatus的值变成Node。.CONDITONAL,在条件队列的尾部加入这个例子。然后释放锁,然后调用LockSupport。.park()进入堵塞状态。

在当前线程释放锁之前,当前线程必须是同步队列的第一个节点。释放锁后,当前线程从同步队列中排队。

被唤醒后,再次尝试获得锁。获得锁后退出await()方法。

六、Conditionsignal()主流程

signal()首先判断当前线程是否占用锁,然后才能继续占用锁。然后将条件队列第一节点的waitstatus放置为0,然后从条件队列中排队。出队后的节点不会被垃圾回收,而是加入同步队列的尾部,将同步队列倒数第二节点的waitstatus放置为-1,然后使用locksupport.unpark()唤醒以团队节点为代表的线程。

上述分析忽略了响应中断的过程。欢迎讨论、质疑和纠正。

上一篇 @RestController注解开发问题排查
下一篇 游戏三级分类部分代码

文章素材均来源于网络,如有侵权,请联系管理员删除。