AQS原理探究
AQS原理探究
AQS维持了一个单一的原子变量--state,通过getState、setState、compareAndSetState可以操作它的值。
- 对于重入锁ReentrantLock的实现来说,state可以用来表示当前线程获取锁的可重入次数
- 对于读写锁ReentrantReadWriteLock来说,state的高16位表示获取该读锁的次数,低16位表示获取该写锁的次数
- 对于信号量Semaphore来说,state表示当前可用信号的次数
- 对于FutuerTask来说,state表示任务状态(还没开始、运行、完成、取消)
- 对于CountDownLatch和CyclicBarrier来说,state用来表示计算器当前的值
AQS结点
AQS是一个FIFO的双向队列。
static final class Node {
/** Marker to indicate a node is waiting in shared mode */
//共享模式,标记该线程是获取共享资源时候被阻塞后挂起放入AQS队列
static final Node SHARED = new Node();
/** Marker to indicate a node is waiting in exclusive mode */
//独占模式:标记该线程是获取独占资源时候被阻塞挂起放入AQS队列
static final Node EXCLUSIVE = null;
/** waitStatus value to indicate thread has cancelled */
static final int CANCELLED = 1;
/** waitStatus value to indicate successor's thread needs unparking */
static final int SIGNAL = -1;
/** waitStatus value to indicate thread is waiting on condition */
static final int CONDITION = -2;
/**
* waitStatus value to indicate the next acquireShared should
* unconditionally propagate
*/
static final int PROPAGATE = -3;
/**
* The field is initialized to 0 for normal sync nodes, and
* CONDITION for condition nodes. It is modified using CAS
* (or when possible, unconditional volatile writes).
*/
/**
* 记录当前线程的状态:
* signal: 线程需要被唤醒(unpark)
* cancelled:线程被取消了(due to timeout or interrupt)
* condition:线程在队列里等待
* propagate 释放共享资源时候需要通知其他结点
*/
volatile int waitStatus;
//记录当前结点的前驱结点
volatile Node prev;
//记录当前结点的后继结点
volatile Node next;
//存入放入AQS队列中的线程
volatile Thread thread;
//存储condition队列中的后继节点。
//若nextWaiter=SHAERD,则CLH队列是 独享锁 队列
//如nextWaiter=EXCLUSIVE(null),则CLH为 共享锁 队列
Node nextWaiter;
/**
* Returns true if node is waiting in shared mode.
*/
//共享锁返回true
final boolean isShared() {
return nextWaiter == SHARED;
}
//获取前驱结点
final Node predecessor() throws NullPointerException {
Node p = prev;
if (p == null) //null则抛出NullPointerException
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; //mode表示是独享锁还是共享锁
this.thread = thread; //结点对应的线程
}
Node(Thread thread, int waitStatus) { // Used by Condition
this.waitStatus = waitStatus; //线程的等待状态
this.thread = thread; //结点对应的线程
}
}
AQS内部类ConditionObject
ConditionObejct是用来结合锁实现线程同步,其可直接访问AQS对象内部的变量,如state、AQS队列;ConditionObejct是条件变量,每个条件变量对应着一个条件队列(单向链表队列),用来存放调用条件变量的await()方法后被阻塞的线程。
-
独占模式下获取和释放资源的方法
void acquire(int arg)
void acquireInterruptibly(int arg)
boolean release(int arg)
/** * 当一个线程调用acquire()方法来获取多占资源的时候, * 1.调用tryAcquire尝试获取资源 * 2. 具体是设置状态变量state的值(原子操作UnSafe类实现),成功则直接返回,失败则将当前线程封装为类型为Node.Exclusive的Node结点后插入到AQS阻塞队列尾部 * 3.调用 * */ public final void acquire(int arg) { if (!tryAcquire(arg) && acquireQueued(addWaiter(Node.EXCLUSIVE), arg)) selfInterrupt(); } public final void acquireInterruptibly(int arg) throws InterruptedException { if (Thread.interrupted()) throw new InterruptedException(); if (!tryAcquire(arg)) doAcquireInterruptibly(arg); } //释放锁 public final boolean release(int arg) { if (tryRelease(arg)) { Node h = head; if (h != null && h.waitStatus != 0) //唤醒结点 unparkSuccessor(h); return true; } return false; }
acquire深入分析
//线程尝试获得锁,如果成功就直接返回,不成功则创建一个Node结点并添加到CLH队列中。TryAcquire尝试获得锁,addWriter则创建节点并添加到CLH到队列中。其中tryAcquire并没有实现,仅仅是抛出一个异常,需要锁自己实现
protected boolean tryAcquire(int arg) {
throw new UnsupportedOperationException();
}
//产生一个中断。
static void selfInterrupt() {
Thread.currentThread().interrupt();
}
//主要是根据该结点找到CLH的头节点,并尝试获得锁,判断是否需要挂起,并返回挂起标识。
final boolean acquireQueued(final Node node, int arg) {
boolean failed = true;
try {
boolean interrupted = false;
for (;;) {
final Node p = node.predecessor();
if (p == head && tryAcquire(arg)) {
setHead(node);
p.next = null; // help GC
failed = false;
return interrupted;
}
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
interrupted = true;
}
} finally {
if (failed)
cancelAcquire(node);
}
}
-
共享状态下获取和释放资源的方法
void acquire(int arg)
void acquireInterruptibly(int arg)
boolean release(int arg)
public final void acquireShared(int arg) { if (tryAcquireShared(arg) < 0) doAcquireShared(arg); } public final void acquireSharedInterruptibly(int arg) throws InterruptedException { if (Thread.interrupted()) throw new InterruptedException(); if (tryAcquireShared(arg) < 0) doAcquireSharedInterruptibly(arg); } public final boolean releaseShared(int arg) { if (tryReleaseShared(arg)) { doReleaseShared(); return true; } return false; }