设为首页 加入收藏

TOP

10分钟从源码级别搞懂AQS(AbstractQueuedSynchronizer)(一)
2023-09-09 10:25:55 】 浏览:128
Tags:10分 钟从源 别搞懂 AQS AbstractQueuedSynchronizer
10分钟从源码级别搞懂AQS(AbstractQueuedSynchronizer)

前言

上篇文章15000字、6个代码案例、5个原理图让你彻底搞懂Synchronized有说到synchronized由object monitor实现的

object monitor中由cxq栈和entry list来实现阻塞队列,wait set实现等待队列,从而实现synchronized的等待/通知模式

而JDK中的JUC并发包也通过类似的阻塞队列和等待队列实现等待/通知模式

这篇文章就来讲讲JUC的基石AQS(AbstractQueuedSynchronizer)

需要了解的前置知识:CAS、volatile

如果不了解CAS可以看上篇讲述synchronized的文章(链接在上面)

如果不了解volatile可以看这篇文章 5个案例和流程图让你从0到1搞懂volatile关键字

本篇文章以AQS为中心,深入浅出描述AQS中的数据结构、设计以及获取、释放同步状态的源码流程、Condition等

观看本文大约需要10分钟,可以带着几个问题去观看

  1. 什么是AQS,它是干啥用的?
  2. AQS是使用什么数据结构实现的?
  3. AQS获取/释放同步状态是如何实现的?
  4. AQS除了具有synchronized的功能还拥有什么其他特性?
  5. AQS如何去实现非公平锁、公平锁?
  6. 什么是Condition?它跟AQS是什么关系?

AQS数据结构

什么是AQS呢?

AQS是一个同步队列(阻塞队列),是并发包中的基础,很多并发包中的同步组件底层都使用AQS来实现,比如:ReentrantLock、读写锁、信号量等等...

AQS有三个重要的字段,分别是: head 头节点、tail 尾节点、state 同步状态

public abstract class AbstractQueuedSynchronizer
    extends AbstractOwnableSynchronizer
    implements java.io.Serializable {
    //头节点
    private transient volatile Node head;
    //尾节点
    private transient volatile Node tail;
    //同步状态
    private volatile int state;   
}    

头尾节点很好理解,因为AQS本身就是个双向链表,那么state同步状态是什么?

AQS中使用同步状态表示资源,然后使用CAS来获取/释放资源,比如设置资源为1,一个线程来尝试获取资源,由于同步状态目前为1,于是该线程CAS替换同步状态为0,成功后表示获取到资源,之后其他线程再来获取资源就无法获取了(状态为0),直到获取资源的线程来释放资源

上述获取/释放资源也可以理解成获取/释放锁

同时三个字段都被volatile修饰,用volatile来保证内存可见性,防止其他线程修改这些数据时当前线程无法感知

通过上面的描述,我们可以知道AQS大概长这样

image.png

当某个线程获取资源失败时,会被构建成节点加入AQS中

节点Node是AQS中的内部类,Node中有些重要的字段一起来看看

static final class Node {
        //节点状态
        volatile int waitStatus;
    
        //前驱节点
        volatile Node prev;
?
        //后继节点
        volatile Node next;
        
        //当前节点所代表的线程
        volatile Thread thread;
?
        //等待队列使用时的后继节点指针
        Node nextWaiter;
}

prev、next、thread应该都好理解

AQS同步队列和等待队列都使用这种节点,当等待队列节点被唤醒出队时,方便加入同步队列

nextWaiter就是用于节点在等待队列中指向下一个节点

waitStatus表示节点的状态

状态 说明
INITIAL 0 初始状态
CANCELLED 1 该节点对应的线程取消调度
SIGNAL -1 该节点对应的线程阻塞,等待唤醒竞争资源
CONDITION -2 该节点在等待(条件)队列中,等待唤醒后从等待队列出队进入同步队列竞争
PROPAGATE -3 共享情况下,会唤醒后续所有共享节点

不太理解状态不要紧,我们后文遇到再说

经过上面的描述,节点大概是长成这样的

image.png

AQS中还有另外一个内部类ConditionObject用于实现等待队列/条件队列,我们后文再来说说

AQS中可以分为独占、共享模式,其中这两种模式下还可以支持响应中断、纳秒级别超时

独占模式可以理解为同一时间只有一个线程能够获取同步状态

共享模式可以理解为可以有多个线程能够获取同步状态,方法中常用shared标识

方法中常用acquire标识获取同步状态,release标识释放同步状态

image.png

这些方法都是模板方法,规定流程,将具体的实现留给实现类去做(比如获取同步状态,该如何获取交给实现类去实现)

独占式

独占式实际就是时刻上只允许一个线程独占该资源,多线程竞争情况下也只能有一个线程获取同步状态成功

获取同步状态

不响应中断的独占获取和响应中断、超时的类似,我们以acquire为例查看源码

    public final void acquire(int arg) {
        if (!tryAcquire(arg) &&
            acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
            selfInterrupt();
    }

tryAcquire 方法用于尝试获取同步状态,参数arg表示获取多少同步状态,获取成功返回true 则会退出方法,留给实现类去实现

addWaiter

addWaiter(Node.EXCLUSIVE) 构建独占式节点,并用CAS+失败重试的方式加入AQS的末尾

    private Node addWaiter(Node mode) {
        //构建节点
        Node node = new Node(Thread.currentThread(), mode);
        //尾节点不为空则CAS替换尾节点
        Node pred =
首页 上一页 1 2 3 4 5 6 7 下一页 尾页 1/7/7
】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
上一篇Nacos 注册中心的设计原理:让你.. 下一篇电商类面试问题--01Elasticsearch..

最新文章

热门文章

Hot 文章

Python

C 语言

C++基础

大数据基础

linux编程基础

C/C++面试题目