AQS(AbstractQueuedSynchronizer) 学习
AbstractQueuedSynchronizer
本身是个抽象类。
在这个抽象类里,有两个内部类,一个是 Node,一个 ConditionObject 类。
还有其他的一些属性变量。
一、AQS 成员变量
//Head of the wait queue, lazily initialized. Except for initialization, it is modified only via method setHead. Note: If head exists, its waitStatus is guaranteed not to be CANCELLED.
private transient volatile Node head;
/**
* Tail of the wait queue, lazily initialized. Modified only via
* method enq to add new wait node.
*/
private transient volatile Node tail;
/**
* The synchronization state.
* 同步状态
* 如果 state=0,则锁未被线程获取;若 state≠0,说明锁已经被获取。不同的类实现也不一样,比如读写锁的读锁,可以允许多个线程获取锁
*/
private volatile int state;
// 获取同步状态
protected final int getState() {
return state;
}
//设置同步状态
protected final void setState(int newState) {
state = newState;
}
// 使用CAS 设置当前状态,该方法能够保证状态设置的原子性
protected final boolean compareAndSetState(int expect, int update) {
// See below for intrinsics setup to support this
return unsafe.compareAndSwapInt(this, stateOffset, expect, update);
}
自定义同步组件实现的两个步骤:
1、自定义类实现 Lock接口
2、自定义类内部创建静态内部类Sync,这个静态内部类继承 AQS,并重写其获取锁、释放锁的状态。
3、将自定义同步组件类的获取锁和释放锁的操作,代理到Sync 上。
二、AQS 同步器的工作原理
- 独占锁:独占锁就是同一时刻只能有一个线程获取到锁,而其他获取锁的线程只能处于同步队列中等待,只有获得锁的线程释放了锁,后继的线程才能够释放锁。
- 共享锁:
Node 节点
static final class Node {
// 有共享模式和独占模式,默认是共享模式
static final Node SHARED = new Node();
static final Node EXCLUSIVE = null;
// waitStatus的 4 种状态,描述的是当前队列中节点中的线程的四种状态
static final int CANCELLED = 1;
static final int SIGNAL = -1;
static final int CONDITION = -2;
static final int PROPAGATE = -3;
// 下一个线程的等待状态,为 0 则为唤醒状态
volatile int waitStatus;
// 当前节点的前驱节点
volatile Node prev;
// 当前节点的后驱节点
volatile Node next;
// 当前节点的线程
volatile Thread thread;
// 当前节点的下一个节点是哪种模式
Node nextWaiter;
/**
* Returns true if node is waiting in shared mode.
*/
final boolean isShared() {
return nextWaiter == SHARED;
}
// 返回当前节点的前驱节点
final Node predecessor() throws NullPointerException {
Node p = prev;
if (p == null)
throw new NullPointerException();
else
return p;
}
Node() {
// Used to establish initial head or SHARED marker
}
Node(Thread thread, Node mode) {
// Used by addWaiter
this.nextWaiter = mode;
this.thread = thread;
}
Node(Thread thread, int waitStatus) {
// Used by Condition
this.waitStatus = waitStatus;
this.thread = thread;
}
}
三、独占式同步状态获取与释放
以可重入锁 ReentrantLock 为例:
1、ReentrantLock#lock()方法
public void lock() {
sync.lock();
}
下面以公平锁的实现进行学习:
2、ReentrantLock.FairSync#lock
final void lock() {
acquire(1);
}
下面进行调用分析:
3、AbstractQueuedSynchronizer#acquire(int arg)方法
java.util.concurrent.locks.AbstractQueuedSynchronizer#acquire
// 获取锁,调用tryAcquire获取锁,如果获取锁失败,即返回 false
// 返回 false,则将当前线程的等待状态设置为EXCLUSIVE,同时将其加入到同步队列当中等待,当前线程自己中断
public final void acquire(int arg) {
// 1、tryAcquire :同步状态的获取
// 2、节点构造
// 3、addWaiter:加入同步队列
// 4、acquireQueued:在同步队列中自旋
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
// 自己中断
selfInterrupt();
}
上面的主要流程是:
- 1、调用方法:tryAcquire,尝试获取锁
- 2、如果获取锁失败,则调用方法:addWaiter,将当前线程加入到等待队列当中,同时设置当前线程的状态为:EXCLUSIVE
- 3、执行acquireQueued方法,不断自旋获取锁,
- 4、如果获取不到锁,则将当前线程中断。
3.1 tryAcquire(int acquires) 方法
java.util.concurrent.locks.ReentrantLock.FairSync#tryAcquire
下面一一来看下里面的方法:
tryAcquire(int acquires)
方法是获取锁,如果获取锁失败则返回 flase
。该方法有公平锁和非公平锁两种实现
// 公平锁获取
protected final boolean tryAcquire(int acquires) {
// 获取当前线程
final Thread current = Thread.currentThread();
// 先获取同步状态
int c = getState();
// 如果当前同步状态为 0,说明锁还没有被获取
if (c == 0) {
// 若同步状态符合预期,则进行 CAS 设置同步状态(初次设置同步状态是CAS 进行设置)
// 观察下面的非公平锁的实现,发现两者获取锁时,代码多了一个hasQueuedPredecessors()方法
// 这个方法用于判断当前线程之前是否有等待线程,如果没有的话,就会执行 cas 操作进行加锁
if (!hasQueuedPredecessors() &&
compareAndSetState(0, acquires)) {
// 将当前线程设置给exclusiveOwnerThread线程,表示同步状态已被当前线程独占式获取
setExclusiveOwnerThread(current);
// 表示已独占式获取到锁
return true;
}