JUC中的AQS底层详细超详解

摘要:当你使用java实现一个线程同步的对象时 , 一定会包含一个问题:你该如何保证多个线程访问该对象时 , 正确地进行阻塞等待 , 正确地被唤醒?
本文分享自华为云社区《JUC中的AQS底层详细超详解 , 剖析AQS设计中所需要考虑的各种问题!》 , 作者: breakDawn。
java中AQS究竟是做什么的?当你使用java实现一个线程同步的对象时 , 一定会包含一个问题:
你该如何保证多个线程访问该对象时 , 正确地进行阻塞等待 , 正确地被唤醒?
关于这个问题 , java的设计者认为应该是一套通用的机制
因此将一套线程阻塞等待以及被唤醒时锁分配的机制称之为AQS
全称 AbstractQuenedSynchronizer
中文名即抽象的队列式同步器。
基于AQS , 实现了例如ReentenLock之类的经典JUC类 。
AQS简要步骤
  1. 线程访问资源 , 如果资源足够 , 则把线程封装成一个Node , 设置为活跃线程进入CLH队列 , 并扣去资源
  2. 资源不足 , 则变成等待线程Node , 也进入CLH队列
  3. CLH是一个双向链式队列, head节点是实际占用锁的线程 , 后面的节点则都是等待线程所对应对应的节点
AQS的资源statestate定义AQS中的资源是一个int值 , 而且是volatile的 , 并提供了3个方法给子类使用:
private volatile int state;protected final int getState() { return state;}protected final void setState(int newState) {state = newState;}// cas方法compareAndSetState(int oldState, int newState);如果state上限只有1 , 那么就是独占模式Exclusive , 例如 ReentrantLock
如果state上限大于1 , 那就是共享模式Share , 例如 Semaphore、CountDownLatch、ReadWriteLock , CyclicBarrier
已经有CAS方法了 , 为什么资源state还要定义成volatile的?对外暴露的getter/setter方法 , 是走不了CAS的 。而且setter/getter没有被synchronized修饰 。所以必须要volatile , 保证可见性
这样基于AQS的实现可以直接通过getter/setter操作state变量 , 并且保证可见性 , 也避免重排序带来的影响 。比如CountDownLatch , ReentrantReadWriteLock , Semaphore都有体现(各种getState、setState)
对资源的操作什么时候用CAS , 什么使用setState?volatile的state成员有一个问题 , 就是如果是复合操作的话不能保证复合操作的原子性
因此涉及 state增减的情况 , 采用CAS
如果是state设置成某个固定值 , 则使用setState
AQS的CLH队列为什么需要一个CLH队列这个队列的目的是为了公平锁的实现
即为了保证先到先得 , 要求每个线程封装后的Node按顺序拼接起来 。
CLH本质?是一个Queue容器吗不是的 , 本质上是一个链表式的队列
因此核心在于链表节点Node的定义
JUC中的AQS底层详细超详解

文章插图
除了比较容易想到的prev和next指针外
还包含了该节点内的线程
以及 waitStatus 等待状态
4种等待状态如下: