AbstractQuenedSynchronizer
抽象的队列式同步器。基于 自旋
+CAS
+volatile
AQS的核心思想是,如果被请求的共享资源空闲,则将当前请求资源的线程设置为有效的工作线程,并将共享资源设置为锁定状态,如果被请求的共享资源被占用,那么就需要一套线程阻塞等待以及被唤醒时锁分配的机制,这个机制AQS是用CLH队列锁实现的,即将暂时获取不到锁的线程加入到队列中。
CLH(Craig,Landin,and Hagersten)队列是一个虚拟的双向队列,虚拟的双向队列即不存在队列实例,仅存在节点之间的关联关系。
AQS是将每一条请求共享资源的线程封装成一个CLH锁队列的一个结点(Node),来实现锁的分配。
AQS框架 - 管理状态
AQS
内部维护属性:volatile int state(32位)``````state
表示资源的可用状态state
三种访问方式getState()
、setState()
、compareAndSetState()
AQS
定义两种资源共享方式
Exclusive
: 独占,只有一个线程能执行,如ReetrantLock
Share
: 共享,多个线程可以同时执行,如Semaphore
、CountDownLatch
、ReadWriteLock
、CyclicBarrier
。AQS
定义两种队列
● 同步等待队列
● 条件等待队列
基于模板方法模式提供了获取资源和释放资源的模板方法,至于具体线程等待队列的维护,AQS已经在顶层实现好了。自定义同步器实现的时候主要实现下面几种方法:
isHeldExclusively()
:该线程是否正在独占资源。只有用到condition才需要去实现它。
tryAcquire(int)
:独占方式。尝试获取资源,成功则返回true,失败则返回false。
tryRelease(int)
:独占方式。尝试释放资源,成功则返回true,失败则返回false。
tryAcquireShared(int)
:共享方式。尝试获取资源。负数表示失败;0表示成功,但没有剩余可用资源;正数表示成功,且有剩余资源。
tryReleaseShared(int)
:共享方式。尝试释放资源,如果释放后允许唤醒后续等待结点返回true,否则返回false。
Java中断机制
Java中断机制是一种协作机制,通过 Thread.interrupt()
并不能直接中断一个线程,只是将中断标志设置为true,需要被中断的线程自己去处理中断。
interrupt()
: 将中断标记位设置为true。
isInterrupted()
: 获取中断标记位。
interrupted()
:获取中断标记位,并将标记位重置。
throw InterruptException
: 抛出该异常的同时,会重置中断标志位。
公平锁FairSync(ReentrantLock)
AbstractQueuedSynchronizer.acquire(int arg)
此方法是独占模式下线程获取共享资源的顶层入口。如果获取到资源,线程直接返回,否则进入等待队列,直到获取到资源为止,且整个过程忽略中断的影响。
/**
* 以独占模式获取,忽略中断。通过至少调用一次tryAcquire来实现,成功时返回。
* 否则,线程将进入队列,可能会反复阻塞和解除阻塞,调用tryAcquire直到成功。这个方法可以用来实现Lock.lock *方法
* 1、tryAcquire()尝试直接去获取资源,如果成功则直接返回(这里体现了非公平锁,每个线程获取锁时会尝试直接抢占加塞一次,而CLH队列中可能还有别的线程在等待)
* 2、addWaiter()将该线程加入等待队列的尾部,并标记为独占模式
* 3、acquireQueued()使线程阻塞在等待队列中获取资源,一直获取到资源后才返回。如果在整个等待过程中被中断过,则返回true,否则返回false
* 4、如果线程在等待过程中被中断过,它是不响应的。只是获取资源后才再进行自我中断selfInterrupt(),将中断补上
*/
public final void acquire(int arg) {
// 尝试加锁
// 加锁失败进入队列
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
AbstractQueuedSynchronizer.tryAcquire(int arg)
模板方法。尝试去获取独占资源。如果获取成功,则直接返回true,否则直接返回false。
AQS只是一个框架,具体资源的获取/释放方式交由自定义同步器去实现
protected boolean tryAcquire(int arg) {
throw new UnsupportedOperationException();
}
ReentrantLock.tryAcquire(int acquires)
ReentrantLock独占锁实现
protected final boolean tryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
// 无锁状态
if (c == 0) {
// 如果当前线程之前有一个队列线程,则为true;如果当前线程位于队列头部或队列为空,则为false
if (!hasQueuedPredecessors() &&
// cas加锁
compareAndSetState(0, acquires)) {
// 设置工作线程
setExclusiveOwnerThread(current);
return true;
}
}
// 可重入锁实现
// state累加,tryRelease的时候递减
else if (current == getExclusiveOwnerThread()) {
int nextc = c + acquires;
if (nextc < 0)
throw new Error("Maximum lock count exceeded");
setState(nextc);
return true;
}
return false;
}
AbstractQueuedSynchronizer.addWaiter(Node mode)
用于将当前线程加入到等待队列的队尾,并返回当前线程所在的结点
private Node addWaiter(Node mode) {
// 以给定模式创建节点
Node node = new Node(Thread.currentThread(), mode);
// Try the fast path of enq; backup to full enq on failure
// 尝试快速方式插入到队尾
Node pred = tail;
if (pred != null) {
node.prev = pred;
if (compareAndSetTail(pred, node)) {
pred.next = node;
return node;
}
}
// enq入队
enq(node);
return node;
}
AbstractQueuedSynchronizer.enq(final Node node)
将node加入队尾
private Node enq(final Node node) {
// CAS + 自旋 ,直到入队成功,cas 修改被volatile修饰的队列属性 tail、head
for (;;) {
Node t = tail;
// 队列为空,创建一个空的标志结点作为head结点,并将tail也指向它。
// 第一次进来初始化第一个空节点,第二次自旋进来走else
if (t == null) { // Must initialize
if (compareAndSetHead(new Node()))
tail = head;
}
// 正常将节点插入到尾部,修改节点prev、next和队列的tail
else {
node.prev = t;
if (compareAndSetTail(t, node)) {
t.next = node;
return t;
}
}
}
}
AbstractQueuedSynchronizer.acquireQueued(final Node node, int arg)
通过 tryAcquire()
和 addWaiter()
,该线程获取资源
1、成功:返回。
2、失败:进入等待状态休息,直到其他线程彻底释放资源后唤醒自己
final boolean acquireQueued(final Node node, int arg) {
//标记是否成功拿到资源
boolean failed = true;
try {
//标记等待过程中是否被中断过
boolean interrupted = false;
// 自旋
for (;;) {
// 拿到prev前置节点
final Node p = node.predecessor();
// 如果前置节点是head,标识处于队列的第二位,为避免LockSupport.park线程切换的资源浪费,直接尝试去获取资源,
// 有可能是节点唤醒的,也有可能被interrupt了
// 如果获取资源成功,将当前节点设为head,抛弃之前的head
if (p == head && tryAcquire(arg)) {
setHead(node);
p.next = null; // help GC
// 成功获取资源
failed = false;
return interrupted;
}
//如果自己可以休息了,就通过park()进入waiting状态,直到被unpark()。如果不可中断的情况下被中断了,那么会从park()中醒过来,发现拿不到资源,从而继续进入park()等待。
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
//如果等待过程中被中断过,哪怕只有那么一次,就将interrupted标记为true
interrupted = true;
}
} finally {
// 如果等待过程中没有成功获取资源(如timeout,或者可中断的情况下被中断了),那么取消结点在队列中的等待。
if (failed)
cancelAcquire(node);
}
}
AbstractQueuedSynchronizer.shouldParkAfterFailedAcquire(Node pred, Node node)
用于状态检查,判断前置节点是否是正常状态,往前遍历,知道找到一个活跃的节点。
整个流程中,如果前驱结点的状态不是SIGNAL,那么自己就不能安心去休息,需要去找个安心的休息点,同时可以再尝试下看有没有机会轮到自己拿号。
private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
// 拿到前置节点等待状态
int ws = pred.waitStatus;
// 前置节点waitStatus是SIGNAL,为活跃节点
if (ws == Node.SIGNAL)
/*
* This node has already set status asking a release
* to signal it, so it can safely park.
*/
return true;
前置节点waitStatus是CANCELLED,往前遍历,直到找到一个SIGNAL
if (ws > 0) {
/*
* Predecessor was cancelled. Skip over predecessors and
* indicate retry.
*/
do {
node.prev = pred = pred.prev;
} while (pred.waitStatus > 0);
pred.next = node;
} else {
/*
* waitStatus must be 0 or PROPAGATE. Indicate that we
* need a signal, but don't park yet. Caller will need to
* retry to make sure it cannot acquire before parking.
*/
// 直接将状态改为SIGNAL
compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
}
return false;
}
AbstractQueuedSynchronizer.parkAndCheckInterrupt()
让线程进入waiting状态。等待前置节点unpark去唤醒。在此状态下,有两种途径可以唤醒该线程:1)被unpark()
;2)被interrupt()
。
private final boolean parkAndCheckInterrupt() {
//调用park()使线程进入waiting状态
LockSupport.park(this);
//如果被唤醒,检查是不是被中断的。
return Thread.interrupted();
}
AbstractQueuedSynchronizer.cancelAcquire(Node node)
此方法主要清除当前节点状态,并将当前节点出队。
private void cancelAcquire(Node node) {
// Ignore if node doesn't exist
if (node == null)
return;
// node不再关联到任何线程
node.thread = null;
// Skip cancelled predecessors
// 跳过被cancel的前继node,找到一个有效的前继节点pred
Node pred = node.prev;
while (pred.waitStatus > 0)
node.prev = pred = pred.prev;
// predNext is the apparent node to unsplice. CASes below will
// fail if not, in which case, we lost race vs another cancel
// or signal, so no further action is necessary.
Node predNext = pred.next;
// Can use unconditional write instead of CAS here.
// After this atomic step, other Nodes can skip past us.
// Before, we are free of interference from other threads.
// 将node的waitStatus置为CANCELLED
node.waitStatus = Node.CANCELLED;
// If we are the tail, remove ourselves.
// 如果node是tail,更新tail为pred,并使pred.next指向null
if (node == tail && compareAndSetTail(node, pred)) {
compareAndSetNext(pred, predNext, null);
} else {
// If successor needs signal, try to set pred's next-link
// so it will get one. Otherwise wake it up to propagate.
int ws;
// 如果node既不是tail,又不是head的后继节点
// 则将node的前继节点的waitStatus置为SIGNAL
// 并使node的前继节点指向node的后继节点
// 跳过当前node节点
if (pred != head &&
((ws = pred.waitStatus) == Node.SIGNAL ||
(ws <= 0 && compareAndSetWaitStatus(pred, ws, Node.SIGNAL))) &&
pred.thread != null) {
Node next = node.next;
if (next != null && next.waitStatus <= 0)
compareAndSetNext(pred, predNext, next);
} else {
// 如果node是head的后继节点,则直接唤醒node的后继节点
unparkSuccessor(node);
}
node.next = node; // help GC
}
}
AbstractQueuedSynchronizer.release(int arg)
此方法是独占模式下线程释放共享资源的顶层入口。它会释放指定量的资源,如果彻底释放了(即state=0),它会唤醒等待队列里的其他线程来获取资源
public final boolean release(int arg) {
//
if (tryRelease(arg)) {
Node h = head;
if (h != null && h.waitStatus != 0)
//唤醒等待队列里的下一个线程
unparkSuccessor(h);
return true;
}
return false;
}
AbstractQueuedSynchronizer.tryRelease(int arg)
跟tryAcquire()一样,这个方法是需要独占模式的自定义同步器去实现的,处理可重入
protected boolean tryRelease(int arg) {
throw new UnsupportedOperationException();
}
AbstractQueuedSynchronizer.unparkSuccessor(Node node)
用于唤醒等待队列中下一个线程,即用unpark()唤醒等待队列中最前边的那个未放弃线程
private void unparkSuccessor(Node node) {
/*
* If status is negative (i.e., possibly needing signal) try
* to clear in anticipation of signalling. It is OK if this
* fails or if status is changed by waiting thread.
*/
int ws = node.waitStatus;
if (ws < 0)
// 置零当前线程所在的结点状态,允许失败
compareAndSetWaitStatus(node, ws, 0);
/*
* Thread to unpark is held in successor, which is normally
* just the next node. But if cancelled or apparently null,
* traverse backwards from tail to find the actual
* non-cancelled successor.
*/
// 拿到后置节点
Node s = node.next;
// 如果后置节点为空或者waitStatus为CANCELLED
if (s == null || s.waitStatus > 0) {
s = null;
// 从后往前找一个,找到最前面的可用的节点,即waitStatus <= 0
for (Node t = tail; t != null && t != node; t = t.prev)
if (t.waitStatus <= 0)
s = t;
}
// 唤醒下一节点
if (s != null)
LockSupport.unpark(s.thread);
}
AbstractQueuedSynchronizer.acquireShared(int arg)
共享模式下线程获取共享资源的顶层入口。它会获取指定量的资源,获取成功则直接返回,获取失败则进入等待队列,直到获取到资源为止。
tryAcquireShared()
需要自定义同步器去实现
public final void acquireShared(int arg) {
// 获取资源,如果小于0,代表没有可用资源,进入等待队列
if (tryAcquireShared(arg) < 0)
doAcquireShared(arg);
}
一节点
if (s != null)
LockSupport.unpark(s.thread);
}
AbstractQueuedSynchronizer.acquireShared(int arg)
共享模式下线程获取共享资源的顶层入口。它会获取指定量的资源,获取成功则直接返回,获取失败则进入等待队列,直到获取到资源为止。
tryAcquireShared()
需要自定义同步器去实现
public final void acquireShared(int arg) {
// 获取资源,如果小于0,代表没有可用资源,进入等待队列
if (tryAcquireShared(arg) < 0)
doAcquireShared(arg);
}