AQS同步器介绍

概述

队列同步器 AbstractQueuedSynchronizer (简称:同步器、AQS),是用来构建锁或者构建其他同步组件的基础框架,它使用了一个 int 成员变量表示同步状态通过内置的 FIFO 队列来完成资源获取线程的排队工作

同步器是一个抽象类,同步器的主要使用方式是继承,子类通过继承同步器并实现其中的抽象方法来管理同步状态。同步状态的改变主要通过三个方法来完成getState(),setState(int newState),compareAndSetStatus(int expect,int update)

子类被推荐定义为自定义同步组件的静态内部类,同步器自身没有实现任何同步接口,它仅仅定义若干同步状态获取释放方法来供自定义同步组件使用

同步器既可以支持独占式获取同步状态,也可以支持共享式获取同步状态,这样就可以方便实现不同类型的同步组件(ReentrantReadWriteLock 和 CountDownLatch)

同步器与锁的关系

同步器是实现锁的关键在锁的实现中聚合同步器利用同步器实现锁的语义

同步器的设计

同步器的设计基于模板方法模式的,即使用者需要继承同步器并重写指定的方法,随后将同步器组合在自定义同步组件的实现中,并调用同步器提供的模板方法,而这些模板方法将调用使用者重写的方法

同步器可重写的方法

 同步器提供的模板方法

 同步器模板方法基本上分为3类:

独占式获取和释放同步状态、共享式获取和释放同步状态查询同步队列中的等待线程情况

队列同步器AQS如何实现同步

主要包括:使用同步队列独占式同步状态获取和释放共享式同步状态获取与释放以及超时获取同步状态等同步器的核心数据结构与模板方法

同步队列

同步器依赖内部的同步队列一个FIFO双向队列)来完成同步状态管理当前线程获取同步状态失败时同步器会将当前线程及等待状态等信息构造为一个节点(Node)并将其加入同步队列同时会阻塞当前线程当同步状态释放时,会把首节点中的线程唤醒,使其再次尝试获取同步状态

同步队列中的节点(Node)用来保存获取同步状态失败的线程引用等待状态以及前驱后后继节点

节点的属性类型与名称以及描述如下表所示:

 节点是构成同步队列的基础同步器拥有首节点(head)和尾节点(tail),没有成功获取同步状态的线程将会成为节点加入该队列的尾部。同步队列的基本结构如下图所示:

 当一个线程成功地获取了同步状态时,其他线程将无法获取到同步状态,转而被构造成为节点并加入到同步队列中,而这个加入队列的过程必须要保证线程安全,因此同步器提供了一个基于CAS的设置尾节点的方法:compareAndSetTail(Node  expect,  Node  update),它需要传递当前线程”认为“的尾节点和当前节点,只有设置成功后,当前节点才正式与之前的尾结点建立连续

 同步队列遵循FIFO(先进先出),首节点是获取同步状态成功的节点首节点的线程在释放同步状态时将会唤醒后继节点,而后继节点将会在获取同步状态成功时将自己设置为首节点

 设置首节点通过获取同步状态成功的线程来完成的,由于只有一个线程能够成功获取到同步状态,因此设置首节点的方法并不需要使用CAS来保证

节点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;
        }
 
        // 构造器1:无参构造器
        Node() {    
        }
 
        // 构造器2:默认构造器
        Node(Thread thread, Node mode) {     // Used by addWaiter
            this.nextWaiter = mode;
            this.thread = thread;
        }
 
        // 构造器3:在条件队列中使用
        Node(Thread thread, int waitStatus) { // Used by Condition
            this.waitStatus = waitStatus;
            this.thread = thread;
        }
    }

其中SHARED和EXCLUSIVE常量分别代表共享模式和独占模式,所谓共享模式一个锁允许多条线程同时操作,如信号量Semaphore采用的就是基于AQS的共享模式实现的,而独占模式则是同一个时间段只能有一个线程对共享资源进行操作,多余的请求线程需要排队等待,如ReentranLock

变量waitStatus表示当前被封装成Node结点的等待状态,共有4种取值CANCELLED、SIGNAL、CONDITION、PROPAGATE

 

AQS中的成员变量

同步队列头结点引用同步队列尾结点引用以及同步状态,

这三个成员变量都使用volatile 关键字进行修饰,这就确保了多个线程对它的修改都是内存可见的。整个类的核心就是这个同步状态,可以看到同步状态其实就是一个int型的变量,大家可以把这个同步状态看成一个密码锁,而且还是从房间里面锁起来的密码锁,state 具体的值就相当于密码控制着密码锁的开合
 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值