AQS核心原理分析

举报
酸菜鱼. 发表于 2022/10/31 19:53:22 2022/10/31
【摘要】 AQS简介AQS位于java.util.concurrent.locks包下,其全称是AbstractQueuedSynchronizer,即抽象队列同步器,是阻塞式锁和相关的同步器工具的框架。在AQS内部,主要维护了一个基于FIFO(First Input First Output)的等待队列,类似于前面讲到的monitor锁的WaitSet集合。 AQS底层数据结构同时,双向链表中的每...

AQS简介

AQS位于java.util.concurrent.locks包下,其全称是AbstractQueuedSynchronizer,即抽象队列同步器,是阻塞式锁和相关的同步器工具的框架。

在AQS内部,主要维护了一个基于FIFO(First Input First Output)的等待队列,类似于前面讲到的monitor锁的WaitSet集合。

AQS底层数据结构

同时,双向链表中的每个节点都是对线程的封装,对于每个节点,都分别指向各自的直接前驱节点和直接的后继节点。当然,也可以对任意一个节点进行遍历。

在多线程并发情况下,当线程竞争锁失败后,会被封装成一个Node节点,加入到AQS队列的末尾,当当前获取锁的对象释放锁后,会在AQS队列中唤醒一个被阻塞的线程

当然,在AQS队列中,还维护了一个被volatile关键字修饰的变量,它的名字叫做state,state,在中文中有状态的意思,因此state,记录了每个线程的状态。当然,其对应有getState方法–获取state状态,setState方法–设置state状态。多线程情况下,可想而知,肯定会出现线程安全问题,但是,but,设置state状态时,会用到CAS乐观锁机制,调用unsafe类中的方法,保证线程的安全,提供了compareAndSetState方法保证了多线程修改state变量的原子性,(CAS乐观锁在前面文章中记录过)如下面的源码:

    //使用volatile关键字修饰
    private volatile int state;
    //getState方法
    protected final int getState() {
        return state;
    }
    //setState方法
    protected final void setState(int newState) {
        state = newState;
    }
    //compareAndSetState方法
    protected final boolean compareAndSetState(int expect, int update) {
        // See below for intrinsics setup to support this
        return unsafe.compareAndSwapInt(this, stateOffset, expect, update);
    }

回想Synchronized锁中,有WaitSet和EntryList集合,而在AQS中,当然也有条件变量队列,即condition队列。条件变量可以来实现等待,唤醒机制,不同的是,AQS条件变量中,支持多个条件变量。而在synchronized锁中,使用notifyAll()方法,会一次性将所有阻塞的线程全部唤醒。

AQS对底层锁的支持(Node类)

AQS支持独占锁和共享锁两种模式。独占锁是同时只有一个线程能够访问资源,如基于AQS实现的Reentrantlock锁(这个知识后面会详细讲解)。而共享锁可以同时允许多个线程访问资源

而独占锁和共享锁模式都是在静态内部类Node中定义的,去掉注释后,Node类的源码如下(包括注释):

static final class Node {
        static final Node SHARED = new Node();//表示当前线程为共享模式
        static final Node EXCLUSIVE = null;//表示当前线程为独占模式
        static final int CANCELLED =  1; 
        static final int SIGNAL    = -1;
        static final int CONDITION = -2;
        static final int PROPAGATE = -3;
        volatile int waitStatus;
        volatile Node prev;//前驱节点
        volatile Node next;//后继节点
        volatile Thread thread;
        Node nextWaiter;
        
        //判断当前节点是否为共享节点
        final boolean isShared() {
            return nextWaiter == SHARED;
        }
        
        //查找前置节点
         final Node predecessor() throws NullPointerException {
            Node p = prev;
            if (p == null)
                throw new NullPointerException();
            else
                return p;
        }
        
        // Used to establish initial head or SHARED marker,无参构造
        Node() {    
        }
        
        // Used by addWaiter,创建有条件队列的节点
        Node(Thread thread, Node mode) {     
            this.nextWaiter = mode;
            this.thread = thread;
        }
        
        // Used by Condition ,带有初始waitStatus状态的节点
        Node(Thread thread, int waitStatus) { 
            this.waitStatus = waitStatus;
            this.thread = thread;
        }
    }

解释:

注意:对于AQS,阻塞队列和条件队列是不一样的。阻塞队列是采用双向链表保存的,有pre和next两个属性,而对于上文说的条件队列Condition,是用nextWaiter来链接的,表示当前节点唤醒后,依据该节点的状态,判断是以共享模式,还是以独占模式,其他条件等方式来唤醒下一个线程(节点)

CANCELLED:值为1,在同步队列中等待的线程等待超时或被中断,需要从同步队列中取消该Node的结点,其结点的waitStatus为CANCELLED,表示结束状态,进入此状态后的结点将不会再变化。

SIGNAL:值为-1,被标识为该等待唤醒状态的后继结点,当其前继结点的线程释放了同步锁或被取消,将会通知该后继结点的线程执行其实就是处于唤醒状态,只要前继结点释放锁,就会通知标识为SIGNAL状态的后继结点的线程执行

CONDITION:值为-2,与条件变量相关,该标识的结点处于等待队列中,结点的线程等待在Condition上,当其他线程调用了Condition的signal()方法后,CONDITION状态的结点将从等待队列转移到同步队列中,等待获取同步锁。

PROPAGATE:值为-3,与共享模式相关,在共享模式中,该状态表示当前状况下,有资源能够执行后面的acquireShared操作。

由上面代码可知,每个线程(节点包含五个属性:prev,next,thread,waitState,nextWaiter)。其中,变量waitStatus则表示当前被封装成Node结点的等待状态,共有4种取值,默认为0CANCELLED、SIGNAL、CONDITION、PROPAGATE,表示当前线程(节点)处于sync同步队列中,等待获取锁资源

因此,waitState状态有三种:

  1. 当waitState>0时,表示该线程处于取消状态(线程中断或者等待锁超时),需要移除线程;
  2. 当waitState=0时,默认值,表示初始化状态,表示线程还未完成初始化操作;
  3. 当waitState<0,表示有效状态,该线程处于可唤醒状态。

阻塞队列的运行

当有新线程插入队列时,如果插入的队列为null,插入队列后为头结点,则会直接获取资源,开始执行线程,如果不是头结点,在多线程并发情况下,前驱节点进行CAS自旋操作,直到前驱节点执行完成,waitState状态修改为CANCELLED,断开前驱节点的连接,保证原子性更新尾部的节点,获取到资源执行线程。总的来说,头部节点获取资源执行任务,后续节点通过CAS自旋操作查询前面节点是否完成执行,直到头部节点执行完自己的任务,并将waitState状态修改后,通知后续节点获取资源执行任务。

本篇文章就分享到这里了,后续将会分享各种其他关于并发编程的知识,感谢大佬认真读完支持咯 ~

文章到这里就结束了,如果有什么疑问的地方请指出,诸佬们一起讨论🍻
希望能和诸佬们一起努力,今后进入到心仪的公司
再次感谢各位小伙伴儿们的支持🤞

【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。