Java高并发学习——AQS及源码解析

Posted Johnny*

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java高并发学习——AQS及源码解析相关的知识,希望对你有一定的参考价值。

AQS

AQS 是什么

在这里插入图片描述通常的,AbstractQueueSynchronizer类简称AQS。

AbstractQueueSynchronizer字面意思是: 抽象队列 同步器
抽象的,AbstractQueueSynchronizer是JUC的基石,其自身是抽象类,提供模板,供子类实现。

public abstract class AbstractQueuedSynchronizer
extends AbstractOwnableSynchronizer
implements java.io.Serializable

队列
队列的实现形式有单链表和双链表,AbstractQueueSynchronizer使用双链表。队列是对抢不到锁的线程的管理。

同步器

锁和同步器的关系
锁:面向锁的使用者。定义了程序员和锁交互的使用层API,隐藏了实现 细节。调用即可。
同步器:面向锁的实现者。比如Java并发大神DougLee,提出了统一规范并简化了锁的实现。屏蔽了同步 状态管理、阻塞线程排队和通知、唤醒机制等。

AQS 的作用

AQS 是用来构建锁(ReentrantLock)或者其他同步器组件(如CountDownLatch)的重量级框架及整个JUC体系的基石。通过内置的FIFO队列来完成资源获取线程的排队工作,并通过一个int类型变量表示持有锁的状态。

为什么AQS 是JUC的基石

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
可以看到很多的锁(ReentrantLock、ReentrantReadWriteLock等)和同步组件(CountDownLatch、Semaphore信号量)都使用到了AQS,所以说他是JUC的 基石自然而然啦。

抢到资源的线程直接使用处理业务逻辑,抢不到的必然涉及一种排队等候机制。抢占资源失败的线程继续去等待,但等候的线程仍然保留获取锁的可能且获取锁的流程仍在继续,

这个获取锁的流程由CLH队列的变体来实现。它将暂时获取不到锁的线程加入到 队列中,这个队列就是AQS的抽象表现。它将请求共享资源的线程封装成队列的节点(Node),通过CAS、自旋以及LockSupport.park()的方式,维护state变量的状态,是并发达到同步控制的效果。

AbstractQueuedSynchronizer源码分析

AQS组成

在这里插入图片描述
AQS主要包括两部分:

一个表示资源同步状态的volatile的int类型变量 state
一个FIFO 的双向队列CLH:通过该队列完成资源获取的排队工作,将每条要去抢占资源的线程封装成一个Node节点来实现锁的分配,通过CAS来完成对State值的修改。

CLH: 三个大牛组成的名字,是一个单向链表,AQS中的队列使用的是CLH辩题的虚拟双向队列FIFO。

Node内部类

AbstractQueuedSynchronizer有个内部类Node,这个Node的元素是Thread,相当于Node< Thread >。这个与Map类似,Map是通过数组实现的,数组的元素时Node< k, v>。

Node类的内部结构

在这里插入图片描述其中waitStatus是排队队列中每个线程的状态,相当于定后驱每个客户的等待状态。取值为上面的枚举值。

Node类的属性含义

在这里插入图片描述

在这里插入图片描述

AQS同步队列的基本结构

在这里插入图片描述

int整型变量state

state表示同步状态,相当于银行办理业务的受理窗口的状态。
0表示该窗口没线程占用.
1表示该窗口有线程占用,需要等待,

在这里插入图片描述

以ReentrantLock非公平锁为例来理解 AQS

ReentrantLock的内部结构

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

整个ReentrantLock加锁的过程可以分为是三个阶段:

  1. 尝试加锁
  2. 加锁失败,线程进入队列
  3. 线程入队列后,进入阻塞状态。

2.这部分逻辑,是尝试获取锁失败情况下,当前线程(尝试获取锁的线程)会封装成Node对象,加入到AQS队列中的处理逻辑。

初始状态

在这里插入图片描述

在这里插入图片描述

公平锁和非公平锁在获取锁的实现上差别在于,非公平锁比公平锁 少了一个!hashQueuedPredecessors(),该方法用于判断是否需要排队,导致公平锁和非公平锁有如下差异:

公平锁: 公平锁讲究先来后到,线程在获取锁时,如果这个锁的等待队列中已经有线程在等待,那么当前线程就会进入 等待队列中。
非公平锁: 不管是否有等待队列,都会先尝试获取锁。也就是锁队列的第一个排队 线程在unpark()之后还是需要竞争锁的。

以上是关于Java高并发学习——AQS及源码解析的主要内容,如果未能解决你的问题,请参考以下文章

并发编程Java并发编程-看懂AQS的前世今生

Java 并发之AbstractQueuedSynchronizer(AQS)源码解析

聊聊高并发(二十四)解析java.util.concurrent各个组件 深入理解AQS

Java高并发编程实战6,通过AQS源码分析lock()锁机制

Java高并发编程实战6,通过AQS源码分析lock()锁机制

AQS源码学习