原来 AQS实现原理还能如此总结

01 AQS简介

01 什么是AQS

AQS全称为AbstractQueuedSynchronizer,就是抽象队列同步器。AQS是一个用来构建锁和其他同步组件的基础框架,使用AQS可以简单且高效地构造出应用广泛的同步器,它提供了一个FIFO队列,可以看成是一个用来实现同步锁以及其他涉及到同步功能的核心组件。

02 AQS的核心思想

如果被请求的共享资源空闲,则将当前请求资源的线程设置为有效的工作线程,并且将共享资源设置为锁定状态。如果被请求的共享资源被占用,那么就需要一套线程阻塞等待以及被唤醒时锁分配的机制,这个机制AQS是用CLH队列锁实现的,即将暂时获取不到锁的线程加入到队列中。

03 什么是CLH队列

CLH(Craig,Landin,and Hagersten)队列是一个虚拟的双向队列(虚拟的双向队列即不存在队列实例,仅存在结点之间的关联关系)。AQS是将每条请求共享资源的线程封装成一个CLH锁队列的一个结点(Node)来实现锁的分配。

04 同步状态

AQS中的共享资源是使用一个int成员变量来表示同步状态,通过内置的FIFO队列来完成获取资源线程的排队工作。AQS使用CAS对该同步状态进行原子操作实现对其值的修改。状态信息通过procted类型的getState,setState,compareAndSetState进行操作。AQS使用CAS对该同步状态进行原子操作实现对其值的修改。状态信息通过procted类型的getState,setState,compareAndSetState进行操作。
源码:

//同步状态,使用volatile来保证其可见性
private volatile int state;

//获取同步状态
protected final int getState() {
    return state;
}

//设置同步状态
protected final void setState(int newState) {
    state = newState;
}

//原子性地修改同步状态
protected final boolean compareAndSetState(int expect, int update) {
    // See below for intrinsics setup to support this
    return unsafe.compareAndSwapInt(this, stateOffset, expect, update);
}

05 资源的共享方式

AQS定义两种资源共享方式

Exclusive(独占):只能有一个线程能执行,如ReentrantLock。又可分为公平锁和非公平锁:
公平锁:按照线程在队列中的排队顺序,先到者先拿到锁
非公平锁:当线程要获取锁时,无视队列顺序直接去抢锁,谁抢到就是谁的
Share(共享):多个线程可同时执行,如Semaphore、CountDownLatch等。

06 设计模式

AQS的设计是使用模板方法设计模式,它将一些方法开放给子类进行重写,而同步器给同步组件所提供模板方法又会重新调用被子类所重写的方法。
自定义同步器时需要重写下面几个AQS提供的模板方法

//独占式的获取同步状态,实现该方法需要查询当前状态并判断同步状态是否符合预期,然后再进行CAS设置同步状态 
protected boolean tryAcquire(int arg) { throw new UnsupportedOperationException();} 
//独占式的释放同步状态,等待获取同步状态的线程可以有机会获取同步状态 
protected boolean tryRelease(int arg) { throw new UnsupportedOperationException();} 
//共享式的获取同步状态 
protected int tryAcquireShared(int arg) { throw new UnsupportedOperationException();} 
//尝试将状态设置为以共享模式释放同步状态。该方法总是由执行释放的线程调用。 
protected int tryReleaseShared(int arg) { throw new UnsupportedOperationException(); } 
//当前同步器是否在独占模式下被线程占用,一般该方法表示是否被当前线程所独占 
protected int isHeldExclusively(int arg) { throw new UnsupportedOperationException();}

这些方法的实现必须是内部线程安全的。AQS类中的其他方法都是final ,所以无法被其他类覆盖。

02 CLH队列

当共享资源被某个线程占有,其他请求该资源的线程会被阻塞,从而进入同步队列。AQS底层的数据结构采用CLH队列,它是一个虚拟的双向队列,即不存在队列的实例,仅存在节点之间的关联关系。AQS是将每条请求共享资源的线程封装成一个CLH锁队列的一个结点(Node)来实现锁的分配。注:Sync queue,即同步队列,是双向链表,包括head结点和tail结点,结点主要用作后续的调度。而Condition queue不是必须的,它是一个单向链表,只有当使用Condition时,才会存在此单向链表,并且可能会有多个Condition queue。
原来 AQS实现原理还能如此总结
节点的类型是AQS的静态内部类Node,代码:
Node节点的状态有如下四种

  • CANCELLED = 1

表示当前节点从同步队列中取消,即当前线程被取消

  • SIGNAL = -1

表示后继节点的线程处于等待状态,如果当前节点释放同步状态会通知后继节点,使得后继节点的线程能够运行

  • CONDITION = -2

表示当前节点在等待condition,也就是在condition queue中

  • PROPAGATE = -3

表示下一次共享式同步状态获取将会无条件传播下去

03 AQS类图

原来 AQS实现原理还能如此总结
原来 AQS实现原理还能如此总结
AbstractQueuedSynchronizer类继承AbstractOwnableSynchronizer抽象类,实现Serializable接口。

推荐阅读:

  • 深入解析HashMap和ConcurrentHashMap源码以及底层原理

  • 设计模式(二):几种工厂模式详解

  • 进程同步的五种机制以及优缺点(翻译)

  • redis五种数据类型的实现方式,常用命令,应用场景

  • redis和memcahed的共同点,区别以及应用场景

  • 详解TCP的三次握手与四次挥手及面试题(很全面)

  • Arrays 工具类详解(超详细)

  • 算法必须掌握几种方法

  • QPS、TPS、并发用户数、吞吐量

  • 设计模式之单例模式

  • Collections 工具类详解(超详细)

END
如果你喜欢本文,
请长按二维码,关注架构殿堂.
原来 AQS实现原理还能如此总结
转发至 朋友圈,是对我最大的支持。

猜你喜欢

转载自blog.51cto.com/14977428/2545025