AbstractQueuedSynchronizer
在查看ReentrantLock和ReentrantReadWriteLock源码的时候会发现里面会维护一个Sync的对象,根据其注释可以知道,这个是执行所有同步机制的核心工具。而他继承与AbstractQueuedSynchronizer。
AbstractQueuedSynchronizer各种锁的基类。这个类时其他许多同步类的基类,AQS时一个用于构建锁和同步器的框架。很多同步器都可以通过AQS高效的构建出来。AQS解决了在实现同步器时涉及的大量细节问题。AQS来实现独占锁和共享锁的获取和释放
数据的保存
AbstractQueuedSynchronizer中将等待的线程封装在内部类Node里面。
static final class Node {
/** 共享模式下的标记*/
static final Node SHARED = new Node();
/** 用于指示节点正在独占模式下等待的标记*/
static final Node EXCLUSIVE = null;
/** 示当前的线程被取消 */
static final int CANCELLED = 1;
/** waitstatus值指示后续线程需要取消分析 */
static final int SIGNAL = -1;
/** 表示当前节点在等待condition,也就是在condition队列中 */
static final int CONDITION = -2;
/**值为-3,表示当前场景下后续的acquireShared能够得以执行 */
static final int PROPAGATE = -3;
/**使用上述内容*/
volatile int waitStatus;
/** 前置节点 */
volatile Node prev;
/** 后置节点 */
volatile Node next;
/**当前节点所属线程*/
volatile Thread thread;
/** 链接到下一个等待条件的节点,或者共享模式标记*/
Node nextWaiter;
/** 是否为共享模式 */
final boolean isShared() {
return nextWaiter == SHARED;
}
/** 获得前驱 */
final Node predecessor() throws NullPointerException {
Node p = prev;
if (p == null)
throw new NullPointerException();
else
return p;
}
// 用于建立初始头部或共享标记
Node() {
}
// 用于addWaiter使用
Node(Thread thread, Node mode) { // Used by addWaiter
this.nextWaiter = mode;
this.thread = thread;
}
// 用于Condition使用
Node(Thread thread, int waitStatus) { // Used by Condition
this.waitStatus = waitStatus;
this.thread = thread;
}
}
Node实际实现了两个队列,第一个是使用prev和next实现前后节点查询的双向队列,然后使用nextWaiter实现了单向的等待队列。
队列数据的保存
/** 头结点*/
private transient volatile Node head;
/** 尾节点*/
private transient volatile Node tail;
通过维护头尾节点,来保存整个队列信息。同时提供了使用CAS函数设置两者的方法compareAndSetHead
和compareAndSetTail
将当前线程添加到队列中
private Node addWaiter(
Node mode) {
// 创建当前线程的Node对象
Node node = new Node(Thread.currentThread(), mode);
Node pred = tail;
// 如果队列已经创建,就将新节点插入队列尾。
if (pred != null) {
node.prev = pred;
// 使用CAS模式将节点添加至尾部
if (compareAndSetTail(pred, node)) {
pred.next = node;
return node;
}
}
// 如果队列没有创建,通过enq方法创建队列,并插入新的节点。
enq(node);
return node;
}
/**
* 向队列插入节点,
* 返回原先的尾部节点
*/
private Node enq(final Node node) {
for (;;) {
Node t = tail;
// 队列未重置,重置队列,设置头尾节点
if (t == null) {
if (compareAndSetHead(new Node()))
tail ;
} else {
node.prev = t;
if (compareAndSetTail(t, node)) {
t.next = node;
return t;
}
}
}
}
独占模式
多个线程都尝试获取锁的时候,只有一个线程能够获得锁,其他线程被阻塞。释放锁的时候会唤醒正在等待此锁的一个线程
获取锁
public final void acquire(int arg) {
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
当然上面代码可以等价于
public final void acquire(int arg) {
if (tryAcquire(arg)) {
return;
}
Node node = addWaiter(Node.EXCLUSIVE);
boolean b = acquireQueued(node, arg);
if (b) {
selfInterrupt();
}
}
- 先调用tryAcquire尝试获得独占锁,返回true,表示获取到锁。
- 假如上一步没有获得锁,则使用addWaiter(Node.EXCLUSIVE)创建一个节点,然后添加至队列中。
- 然后调用acquireQueued方法去获取锁。
- 假如acquireQueued返回true则表示在获取锁的过程中线程被中断了
tryAcquire
尝试获得独占锁
protected boolean tryAcquire(int arg) {
throw new UnsupportedOperationException();
}
AbstractQueuedSynchronizer中并没有实现它,而是交给了子类。那么我们看下ReentrantLock中NonfairSync的实现
protected final boolean tryAcquire(int acquires) {
return nonfairTryAcquire(acquires);
}
final boolean nonfairTryAcquire(int acquires) {
// 获得当前线程
final Thread current = Thread.currentThread();
// 获取锁的记录状态
int c = getState();
// 空闲
if (c == 0) {
// 设置锁状态
if (compareAndSetState(0, acquires)) {
// 设置拥有锁的线程
setExclusiveOwnerThread(current);
return true;
}
}
// 判断当前线程是不是拥有锁的线程
else if (current == getExclusiveOwnerThread()) {
// 当前线程状态值 + acquires
int nextc = c + acquires;
// 数据溢出
if (nextc < 0) // overflow
throw new Error("Maximum lock count exceeded");
// 设置锁新的状态
setState(nextc);
return true;
}
return false;
}
acquireQueued
使用acquireQueued获取锁,返回true,表示在线程等待的过程中,线程被中断了
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;
}
// shouldParkAfterFailedAcquire 判断是否需要阻塞当前线程
// parkAndCheckInterrupt 阻塞当前线程,并在被唤醒后返回线程的中断状态
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
interrupted = true;
}
} finally {
// 将node节点的状态设置成CANCELLED,表示node节点所在线程已取消,不需要唤醒了
if (failed)
cancelAcquire(node);
}
}
假如获取锁失败,并不会返回,因为for(;;)的原因。会继续循环
shouldParkAfterFailedAcquire
shouldParkAfterFailedAcquire 判断是否需要阻塞当前线程
private static boolean shouldParkAfterFailedAcquire(
Node pred, Node node) {
// 获得前置节点的状态
int ws = pred.waitStatus;
// 前一个节点状态是Node.SIGNAL时,就会调用parkAndCheckInterrupt方法,阻塞node线程
// Node.SIGNAL 表示当前节点的后继节点包含的线程需要运行
if (ws == Node.SIGNAL)
return true;
if (ws > 0) {// 如果前一个节点被取消或者唤醒
do {
// 当前节点前一个节点设置为前一个节点的前一个节点,循环操作,直到找到一个没被取消或者唤醒(ws <= 0)
node.prev = pred = pred.prev;
} while (pred.waitStatus > 0);
// 设置前一个节点的后置节点未当前节点
pred.next = node;
} else {// 此时前一个节点pred的状态只能是0或者PROPAGATE,不可能是CONDITION状态
// 将前一个节点pred的状态设置成Node.SIGNAL,这样在下一次循环时,就是直接阻塞当前线程
compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
}
return false;
}
parkAndCheckInterrupt
阻塞当前线程
/**
* 阻塞当前线程
*/
private final boolean parkAndCheckInterrupt() {
// 阻塞当前线程
LockSupport.park(this);
// 返回中断标记,即如果是正常唤醒则返回false,如果是由于中断醒来,就返回true
return Thread.interrupted();
}
假如返回的Thread.interrupted()为true表示为中断操作,在外层会设置interrupted = true
。假如这个时候假如没获得锁则再次挂起,直到获得锁后,此时返回interrupted为true,然后在更上一层selfInterrupt();
中再次触发线程中断。
cancelAcquire
failed == true 的时候表示失败,就不用唤醒线程了。出现这个情况只能是逻辑没有通过
p == head && tryAcquire(arg)
的判断,又因为是循环的会一直尝试,只有一种可能就是tryAcquire抛出了异常。
private void cancelAcquire(Node node) {
// 首先节点必须存在
if (node == null)
return;
// 置空节点线程
node.thread = null;
// 跳过那些已取消的节点,在队列中找到在node节点前面的第一次状态不是已取消的节点
Node pred = node.prev;
while (pred.waitStatus > 0)
node.prev = pred = pred.prev;
// 记录pred原来的下一个节点,用于CAS函数更新时使用
Node predNext = pred.next;
// 设置当前节点状态
node.waitStatus = Node.CANCELLED;
// 如果当前节点为尾部节点,那么设置前置节点为尾部节点
if (node == tail && compareAndSetTail(node, pred)) {
// 设置前置节点尾节点尾null
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;
// 前置节点不是头节点,且状态不是 -1
if (pred != head &&
// 设置前一个节点状态为Node.SIGNAL
((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 {
unparkSuccessor(node);
}
node.next = node; // help GC
}
}
selfInterrupt
中断当前线程
static void selfInterrupt() {
Thread.currentThread().interrupt();
}
释放锁
public final boolean release(int arg) {
// 调用tryRelease方法,尝试去释放锁,由子类具体实现
if (tryRelease(arg)) {
Node h = head;
// 如果队列头节点的状态不是0,那么队列中就可能存在需要唤醒的等待节点。
if (h != null && h.waitStatus != 0)
// 通过unparkSuccessor方法,进而调用LockSupport.unpark(s.thread)方法,唤醒被阻塞的线程
unparkSuccessor(h);
return true;
}
return false;
}
tryRelease
尝试释放独占锁
protected boolean tryRelease(int arg) {
throw new UnsupportedOperationException();
}
AbstractQueuedSynchronizer中并没有实现它,而是交给了子类。那么我们看下ReentrantLock中Sync的实现
protected final boolean tryRelease(int releases) {
// 获得新的锁记录状态
int c = getState() - releases;
// 假如当前线程没有持有锁若进行释放锁则抛出异常
if (Thread.currentThread() != getExclusiveOwnerThread())
throw new IllegalMonitorStateException();
// 假如新的锁状态则返回true,设置锁线程为null
// 否则返回false,证明这个线程还没有完全释放锁
boolean free = false;
if (c == 0) {
free = true;
setExclusiveOwnerThread(null);
}
setState(c);
return free;
}
unparkSuccessor
唤醒node节点的下一个非取消状态的节点
private void unparkSuccessor(Node node) {
// 获取node节点的状态
int ws = node.waitStatus;
// 将当前节点状态设置为0
if (ws < 0)
compareAndSetWaitStatus(node, ws, 0);
Node s = node.next;
// 从队列尾部循环,直到获得最前面非取消状态的节点
if (s == null || s.waitStatus > 0) {
s = null;
for (Node t = tail; t != null && t != node; t = t.prev)
if (t.waitStatus <= 0)
s = t;
}
// 如果s不为null,表示存在非取消状态的节点。那么调用LockSupport.unpark方法,唤醒这个节点
if (s != null)
LockSupport.unpark(s.thread);
}
共享模式
共享模式和独占模式不同之处,支持多个线程获得锁
获取锁
多线程模式下获得锁
public final void acquireShared(int arg) {
// 尝试获取共享锁,如果返回值小于0表示获取共享锁失败
if (tryAcquireShared(arg) < 0)
// 调用doAcquireShared方法去获取共享锁
doAcquireShared(arg);
}
tryAcquireShared
尝试获取共享锁
protected int tryAcquireShared(int arg) {
throw new UnsupportedOperationException();
}
AbstractQueuedSynchronizer中并没有实现它,而是交给了子类。然而ReentrantLock中并没有实现,所以我们需要去ReentrantReadWriteLock中看下实现
protected final int tryAcquireShared(int unused) {
// 获取当前线程
Thread current = Thread.currentThread();
// 获取锁状态
int c = getState();
// 独占锁被占用,且独占锁不是当前线程,可以认为写锁被占用了
if (exclusiveCount(c) != 0 &&
getExclusiveOwnerThread() != current)
return -1;
// 获得读锁次数
int r = sharedCount(c);
// 判断当前线程是否应该被阻塞
if (!readerShouldBlock() &&
// 读锁次数
r < MAX_COUNT &&
// 设置读锁次数
compareAndSetState(c, c + SHARED_UNIT)) {
// 假如读锁为0,则此线程为第一个读取读锁的线程
if (r == 0) {
firstReader = current;
// 计数器设置为1
firstReaderHoldCount = 1;
} else if (firstReader == current) {
// 如果当前线程时第一个读取的线程,且r不为0,则计数器++
firstReaderHoldCount++;
} else {
// 非 firstReader 读锁重入计数更新
// 读锁重入计数缓存,基于ThreadLocal实现
HoldCounter rh = cachedHoldCounter;
// 假如cachedHoldCounter为空,或者cachedHoldCounter的tid和当前线程ID不同
if (rh == null || rh.tid != getThreadId(current))
// 则从ThreadLocal中取出HoldCounter,并设置cachedHoldCounter
cachedHoldCounter = rh = readHolds.get();
else if (rh.count == 0)
// rh为0则向当前线程中设置此HoldCounter
readHolds.set(rh);
// HoldCounter计数器+1
rh.count++;
}
// 返回获得锁
return 1;
}
// 获取读锁失败
// 没有写锁被占用时,尝试通过一次CAS去获取锁时,更新失败(说明有其他读锁在申请)
// 当前线程占有写锁,并且有其他写锁在当前线程的下一个节点等待获取写锁,除非当前线程的下一个节点被取消,否则fullTryAcquireShared也获取不到读锁
return fullTryAcquireShared(current);
}
注意HoldCounter
,为每个线程单独保存的计数器的副本
static final class HoldCounter {
int count = 0;
// Use id, not reference, to avoid garbage retention
final long tid = getThreadId(Thread.currentThread());
}
exclusiveCount
static final int SHARED_SHIFT = 16;
static final int EXCLUSIVE_MASK = (1 << SHARED_SHIFT) - 1;
/** Returns the number of shared holds represented in count */
static int sharedCount(int c) { return c >>> SHARED_SHIFT; }
/** Returns the number of exclusive holds represented in count */
static int exclusiveCount(int c) { return c & EXCLUSIVE_MASK; }
如果从二进制来看EXCLUSIVE_MASK的表示,这个值的低16位全是1,而高16位则全是0,所以exclusiveCount是把state的低16位取出来,表示当前这个锁的写锁加锁次数。
readerShouldBlock
判断当前线程是否应该被阻塞
ReentrantReadWriteLock并未实现,需要去ReentrantReadWriteLock的NonfairSync或者FairSync去查看,查看NonfairSync
final boolean readerShouldBlock() {
return apparentlyFirstQueuedIsExclusive();
}
// head节点不为空,head下一节点不为空,head下一节点是非共享模式的(head下一节点是写线程),head下一节点的线程不为空,返回true 就是需要被阻塞
final boolean apparentlyFirstQueuedIsExclusive() {
Node h, s;
return (h = head) != null &&
(s = h.next) != null &&
!s.isShared() &&
s.thread != null;
}
private transient volatile Node head;
head为队列中等待的头元素
fullTryAcquireShared
用于读取的Acquire的完整版本,用于处理CAS未命中。tryAcquireShared中没有考虑到增加读锁数量的CAS操作compareAndSetState(c, c + SHARED_UNIT)失败的情况,而在fullTryAcquireShared中,如果操作compareAndSetState(c, c + SHARED_UNIT)失败的话,会不断尝试,直到成功
final int fullTryAcquireShared(Thread current) {
HoldCounter rh = null;
for (;;) {
// 和tryAcquireShared一样的逻辑
int c = getState();
if (exclusiveCount(c) != 0) {
if (getExclusiveOwnerThread() != current)
return -1;
} else if (readerShouldBlock()) {
// 如果需要阻塞,说明除了当前线程持有写锁外,还有其他线程已经排队在申请写锁。
// 即使申请读锁的线程已经持有写锁(锁降级)还是会失败,因为有其他线程也在申请写锁,
// 此时,只能结束本次申请读锁的请求,转而去排队,否则,将造成死锁
if (firstReader == current) {
// 空处理,只是确保没有重新获取读锁,不需要重新处理
} else {
// 从readHolds中移除当前线程的持有数,然后返回-1,然后去排队获取读锁
if (rh == null) {
rh = cachedHoldCounter;
if (rh == null || rh.tid != getThreadId(current)) {
rh = readHolds.get();
if (rh.count == 0)
readHolds.remove();
}
}
if (rh.count == 0)
return -1;
}
}
// 申请数溢出
if (sharedCount(c) == MAX_COUNT)
throw new Error("Maximum lock count exceeded");
// 使用CAS模式更新申请数,假如成功进入下面逻辑,假如失败则因为外层循环会再回过来申请
if (compareAndSetState(c, c + SHARED_UNIT)) {
// 申请成功后
if (sharedCount(c) == 0) {
// 设置第一个读取者
firstReader = current;
firstReaderHoldCount = 1;
} else if (firstReader == current) {
// 当前线程时firstReader设置数据
firstReaderHoldCount++;
} else {
// 设置线程readHolds内部数据
if (rh == null)
rh = cachedHoldCounter;
if (rh == null || rh.tid != getThreadId(current))
rh = readHolds.get();
else if (rh.count == 0)
readHolds.set(rh);
rh.count++;
cachedHoldCounter = rh; // cache for release
}
return 1;
}
}
}
doAcquireShared
AQS实现的获取共享锁
private void doAcquireShared(int arg) {
// 为当前线程创建共享锁节点node
final Node node = addWaiter(
Node.SHARED);
boolean failed = true;
try {
boolean interrupted = false;
for (;;) {
final Node p = node.predecessor();
// 如果节点node前一个节点是同步队列头节点。
// 就会调用tryAcquireShared方法尝试获取共享锁
if (p == head) {
int r = tryAcquireShared(arg);
// 如果返回值大于0,表示获取共享锁成功
if (r >= 0) {
setHeadAndPropagate(node, r);
p.next = null; // help GC
// 申请到锁之后假如中断状态就true就证明线程被中断过,传递中断状态
if (interrupted)
selfInterrupt();
failed = false;
return;
}
}
// 如果节点p的状态是Node.SIGNAL,
// 就是调用parkAndCheckInterrupt方法阻塞当前线程,
// 并在被唤醒后返回线程的中断状态
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
interrupted = true;
}
} finally {
// 将node节点的状态设置成CANCELLED,表示node节点所在线程已取消,不需要唤醒了
if (failed)
cancelAcquire(node);
}
}
setHeadAndPropagate
设置队列头,并检查后续任务是否正在等待,如果下一节点为空,或者下一节点为共享节点,则唤醒在等待的共享节点
private void setHeadAndPropagate(Node node, int propagate) {
Node h = head; // Record old head for check below
setHead(node);
/*
*/
if (propagate > 0 || h == null || h.waitStatus < 0 ||
(h = head) == null || h.waitStatus < 0) {
Node s = node.next;
// 下一节点为空,或者下一个节点为共享节点
if (s == null || s.isShared())
// 唤醒等待共享锁的线程
doReleaseShared();
}
}
doReleaseShared
唤醒等待共享锁的线程
private void doReleaseShared() {
/*
*/
for (;;) {
// 将同步队列头赋值给节点h
Node h = head;
// 头结点不为空,且头结点不为尾
if (h != null && h != tail) {
// 获得等待状态
int ws = h.waitStatus;
// 如果状态是Node.SIGNAL,就要唤醒节点h后继节点的线程
if (ws == Node.SIGNAL) {
// 使用CAS设置节点状态为0
if (!compareAndSetWaitStatus(h, Node.SIGNAL, 0))
continue; // loop to recheck cases
// 唤醒节点h后继节点的线程
unparkSuccessor(h);
}
// 如果节点h的状态是0,就设置ws的状态是PROPAGATE。
else if (ws == 0 &&
!compareAndSetWaitStatus(h, 0, Node.PROPAGATE))
continue; // loop on failed CAS
}
// 如果同步队列头head节点发生改变,继续循环, 如果没有改变,就跳出循环
if (h == head) // loop if head changed
break;
}
}
unparkSuccessor
上面已经说过了,就是唤醒一个在等待中的未取消的节点。
释放锁
tryRelease
尝试释放独占锁
public final boolean releaseShared(int arg) {
if (tryReleaseShared(arg)) {
doReleaseShared();
return true;
}
return false;
}
protected boolean tryReleaseShared(int arg) {
throw new UnsupportedOperationException();
}
AbstractQueuedSynchronizer中并没有实现它,而是交给了子类。那么我们看下ReentrantReadWriteLock中的实现
protected final boolean tryReleaseShared(int unused) {
Thread current = Thread.currentThread();
// // 当前线程是第一个获取读锁的线程
if (firstReader == current) {
// 如果是1,那么表示该线程释放读锁,将firstReader设置为null,
// 否则减少计数
if (firstReaderHoldCount == 1)
firstReader = null;
else
firstReaderHoldCount--;
} else {
HoldCounter rh = cachedHoldCounter;
// 获取当前线程的HoldCounter
if (rh == null || rh.tid != getThreadId(current))
rh = readHolds.get();
// 获得其中计数器
int count = rh.count;
// 如果小于等于1,则表示移除数据HoldCounter
if (count <= 1) {
readHolds.remove();
// 如果小于0则表示无锁情况下申请是否锁,抛出异常
if (count <= 0)
throw unmatchedUnlockException();
}
// 修改线程中HoldCounter的计数
--rh.count;
}
for (;;) {
int c = getState();
// 因为读锁是利用高16位储存的,低16位的数据是要屏蔽的,
// 所以这里减去SHARED_UNIT(65536),相当于减一
// 表示一个读锁已经释放
int nextc = c - SHARED_UNIT;
// 使用CAS 设置参数,最后返回设置结果,假如结果不同则表示设置失败,返回false
if (compareAndSetState(c, nextc))
return nextc == 0;
}
}
doReleaseShared
AbstractQueuedSynchronizer中释放共享锁
doReleaseShared方法上面已经说过了其实在获取共享锁之后调用的setHeadAndPropagate
方法中就已经调用了doReleaseShared
这个方法,
没有结束的结束
写到这里其实AbstractQueuedSynchronizerz并没有结束。在释放共享锁和获取独占锁中还有一些状态判断的细节没有说的清楚。以及Condition条件条件这么一大块内容。
本来以为很快就能搞定的东西,却花了超出几倍的时间。写到这里已经快凌晨一点了。剩下的内容怕是以后要另外一部分再来学了。