并发编程系列-同步器 AQS
前言
从AQS到不同锁的实现,jdk提供了比较强大的实现。AQS作为处理线程共享数据时的一种同步器,能够保障共享数据的有序访问。下面几篇系列文章从使用和源码的角度进行分析学习。
> 这篇文章主要先熟悉的AQS同步器系列文章
一、并发编程系列-同步器 AQS
二、并发编程系列-同步器实现一 ReentrantLock
三、并发编程系列-同步器实现二 ReentrantLock Condition
四、并发编程系列-同步器实现三 CountDownLatch
五、并发编程系列-同步器实现四 Semaphore
六、并发编程系列-同步器实现五 CyclicBarrier
什么是AQS
首先AQS的全称:AbstractQueuedSynchronizer,叫做抽象队列同步器
从名字可以得到几个关键信息:
1、它是一个抽象类
2、它应该是基于队列实现的(队列特性 FIFO)
3、可以解决线程同步问题
能力
1、包括两个队列
一个是同步队列,属于双向队列
一个是条件队列,属于单向队列
通过同步队列完成当前节点的状态记录,同时每个节点还对应一个线程对象,可以理解为 每个节点都代表一个线程。
节点的加入,表示线程需要等待获取锁资源,入队列;
节点的退出,表示线程获取到锁资源,出队列进行任务执行
具备队列的特点:FIFO
2、一个状态标识
volatile int state;
是一个整数类型:
0表示资源没有被占用
1表示资源被使用了(具体表示全部还是1个,需要具体实现来定义)
大于1表示本身具有多个竞争资源或重复获取到资源进行了加1
state的更新是基于cas更新的,所以定义了volatile,保障可见性和有序性。
3、实现了锁等待和锁恢复的“原子”能力:
竞争资源 -> 进入队列 -> 睡眠 -> 唤醒 -> 竞争资源(成功的话) -> 出队列 -> 执行任务
而资源的状态state维护交由子类实现,由子类通过state判断是否获取锁及释放锁释放成功,因为state代表一种资源,具体的资源分配情况由具体的实现操作。
结构如何定义
数据结构
因为是基于队列,自然链表是最好的实现方式,上代码
// 静态内部类,作为数据节点
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;
// 还有个节点状态为0,刚竞争资源进入队列的时候的初始状态
// 上面的节点状态均针对当前变量,通过cas更新
volatile int waitStatus;
// 前继节点
volatile Node prev;
// 后继节点
volatile Node next;
// 节点对应的线程信息
volatile Thread thread;
// 下一个等待节点,在条件队列中使用;同时也用来区分是排它锁节点还是共享锁节点的标识
Node nextWaiter;
//// 此处省略了一些构造函数
}
核心变量
// 等待(双向)队列的头节点
private transient volatile Node head;
// 等待(双向)队列的尾节点
private transient volatile Node tail;
// 同步器状态
private volatile int state;
// 继承的变量,排它模式下同步的持有者线程
private transient Thread exclusiveOwnerThread
/*
还有些偏移量值,用于cas时获取对应变量的内存地址偏移量
如:stateOffset = unsafe.objectFieldOffset(AbstractQueuedSynchronizer.class.getDeclaredField("state"));
cas:unsafe.compareAndSwapInt(this, stateOffset, expect, update);
*/
private static final long stateOffset;
private static final long headOffset;
private static final long tailOffset;
private static final long waitStatusOffset;
private static final long nextOffset;
核心方法
// 获取排它锁
public final void acquire(int arg) {}
// 注意:由子类实现,通过state控制尝试获取锁
protected boolean tryAcquire(int arg) {}
// 释放排它锁
public final boolean release(int arg) {}
// 注意:由子类实现,通过维护state尝试释放锁
protected boolean tryRelease(int arg) {}
// 获取共享锁
public final void acquireShared(int arg) {}
// 注意:由子类实现,通过维护state尝试获取锁
protected int tryAcquireShared(int arg) {}
// 释放共享锁
public final boolean releaseShared(int arg) {}
// 注意:由子类实现,通过state控制尝试释放锁
protected boolean tryReleaseShared(int arg) {}
还有一些其它方法:
如可中断式获取锁、超时获取锁(到点中断)
另一个内部类
用于条件队列
public class ConditionObject implements Condition{
// 条件队列第一个节点
private transient Node firstWaiter;
// 条件队列最后一个节点
private transient Node lastWaiter;
//注意:上文提到Node结构中有一个nextWaiter节点,一个使用场景便是条件队列的下一个节点(单链表结构)。
// 当前线程等待,进入条件队列;类似于Object的wait(), 都需要获取到锁后执行
public final void await(){}
public final long awaitNanos(long nanosTimeout){}
// 注意:唤醒基于当前条件等待的一个线程,加入到同步队列中,等待获取锁资源;类似于Object的notify(), 都需要获取到锁后执行
public final void signal() {}
// 唤醒所有条件等待线程,加入到同步队列中。
public final void signalAll() {}
}
signal()唤醒基于当前条件等待的一个节点线程,从第一个开始,加入到同步队列队尾中,等待获取锁资源。
最后
对AQS有了大致的了解后,接下来分享不同的子类实现。
AQS方法的具体源码解读,会在实现的子类中分析,有上下文会有助于理解。