前言:将AQS的知识点做一个总结. 梳理了独占共享两种模式下的加解锁流程,以及两种锁的异同点. 适合日后复习
目录
一、AbstractQueuedSynchronizer
该抽象类主要实现了:在获取锁失败时,线程如何阻塞,如何唤醒.。使用的逻辑结构为【同步等待队列】
如何获取锁,以及获取锁是成功还是失败,由子类去定义。AQS则提供了获取锁失败后,入队/出队、阻塞/唤醒的实现。
队列何时初始化?【尝试加锁】失败后,在addWaiter方法中,若tail为空,则新建一个Node作为头结点
何时入队?【尝试加锁】失败后,尾插到队尾
何时出队?当队列中有新的结点加锁成功时,原head指向的结点出队,随后head指向新的结点
1. 独占锁
加锁
尝试加锁,成功则方法结束;加锁失败则将此线程尾插到"同步等待"队列
// 加锁
public final void acquire(int arg) {
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
protected boolean tryAcquire(int arg) :tryAcquire方法由其实现类实现:返回是否成功获取锁( 即是否成功将state由0变为1 )
private Node addWaiter(Node mode) :将当前线程封装为Node, 并将其插入到队尾. 没初始化队列会先初始化
final boolean acquireQueued(final Node node, int arg) :加锁成功则跳出方法,执行业务逻辑;或park此结点
private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) :pred.ws == -1,则返回true,否则CAS设置pred.ws == -1。并把CANCELLED的结点移除队列
解锁
尝试解锁,解锁失败则直接返回false,成功则唤醒"同步等待"队列中第一个满足要求的结点,并返回true
// 解锁
public final boolean release(int arg) {
if (tryRelease(arg)) {
Node h = head;
if (h != null && h.waitStatus != 0) // ≠0实际上就只能为-1
unparkSuccessor(h); // 唤醒后继结点
return true;
}
return false;
}
private void unparkSuccessor(Node node) :node.waitStatus<0 且 有后继结点,则unpark其后继【LockSupport.unpark(node.next.thread)】;否则取最靠前的、waitStatus<=0的结点
2. 共享锁
加锁
尝试获取锁,返回负数则表示需要进行"阻塞"
public final void acquireShared(int arg) {
if (tryAcquireShared(arg) < 0)
doAcquireShared(arg);
}
private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) :pred.ws == -1,则返回true,否则CAS设置pred.ws==-1。并把CANCELLED的结点移除队列.【同独占锁调用的相同】
private void setHeadAndPropagate(Node node, int propagate) :设置头节点并传播唤醒,也就是说,当一个结点加共享锁成功,它会执行unparkSuccessor(Node node),进而唤醒后继结点。具体实现主要在doReleaseShared()方法👇
private void setHeadAndPropagate(Node node, int propagate) {
Node h = head; // Record old head for check below
setHead(node);
// propagate表示剩余的资源.
if (propagate > 0 || h == null || h.waitStatus < 0 ||
(h = head) == null || h.waitStatus < 0) {
Node s = node.next;
if (s == null || s.isShared())
doReleaseShared();
}
}
/*
简而言之:不断执行unparkSuccessor(h)操作,来唤醒后继结点,
退出条件为h == head,表示如果没有新的结点来代替现有头结点则退出
unparkSuccessor方法同独占锁
*/
private void doReleaseShared() {
for (;;) {
Node h = head;
if (h != null && h != tail) {
int ws = h.waitStatus;
if (ws == Node.SIGNAL) {
if (!h.compareAndSetWaitStatus(Node.SIGNAL, 0))
continue; // loop to recheck cases
unparkSuccessor(h);
}
else if (ws == 0 &&
!h.compareAndSetWaitStatus(0, Node.PROPAGATE))
continue; // loop on failed CAS
}
if (h == head) // loop if head changed
break;
}
}
解锁
尝试解锁,失败返回false;成功则执行doReleaseShared(),该方法会唤醒后继结点
public final boolean releaseShared(int arg) {
if (tryReleaseShared(arg)) {
doReleaseShared();
return true;
}
return false;
}
3. 独占/共享模式对比
加锁成功
共同点:尝试加锁,失败则addWaiter并自旋(加锁->阻塞)
独占锁:boolean tryAcquire,返回true表示加锁成功
共享锁:int tryAcquireShared,返回≥0代表成功
加锁失败
共同点:首次尝试加锁失败,调用addWaiter,并自旋(加锁->阻塞)
在自旋尝试加锁的过程中,若加锁成功,
独占模式下将当前结点设为头结点:head = node,失败则阻塞;
共享模式下,不仅会将当前结点设为头结点,还会在合适条件下(如还有剩余资源),调用doReleaseShared(),来继续唤醒其后继结点。
这也是两种模式最关键的不同
独占模式下,只有release时才会尝试unpark头结点的后继结点;
共享模式下,除release时会unpark外,线程在自旋时获取到锁后,也可能会unpark.
个人理解:doReleaseShared()方法的自旋目的在于,在队列有结点出队时,快速通知下一下结点,使其unpark.
解锁
共同点:唤醒后继结点
独占模式下,head为-1则直接进行unparkSuccessor(h),随后方法结束
共享模式下,在doReleaseShared()中调用unparkSuccessor(h).
代码如下,其中for循环的部分为doReleaseShared
4. 重要属性
waitStatus
static final class Node {
volatile int waitStatus
}
-1:表明后继结点需要被唤醒;结点想要被park,也需要其前驱结点为-1。在上述加解锁的方法里被置为-1的时机有:
- shouldParkAfterFailedAcquire(Node pred, Node node)中,对于pred<=0&&≠-1,将pred.ws置为-1
0:初始状态. 结点刚创建时的状态。在上述加解锁的方法里被置为0的时机有:
- unparkSuccessor(Node node)中,如果ws<0, 将ws置为0【这一步我不知道有什么意义...】
- doReleaseShared()中,调用unparkSuccessor(h)前,会将-1变为0。【有线程安全检查的作用,但不知道为何要置0】
猜测:让即将被唤醒的线程成为头节点后,ws为0,这样后续结点可以多一次循环拿锁的机会,减小被park的概率
state
public abstract class AbstractQueuedSynchronizer extends AbstractOwnableSynchronizer implements java.io.Serializable {
private volatile int state;
}
独占模式:0:空闲;1:被占有;≥1:被重入的次数
共享模式:state:资源数,0表示没有可用资源
二、ReentrantLock
ReentrantLock类结构
ReentrantLock源码
加锁:区分公平和非公平两种模式
// ReentrantLock类
public void lock() {
sync.acquire(1);
}
------------------------------非公平锁tryAcquire------------------------------
// NonfairSync类
protected final boolean tryAcquire(int acquires) {
return nonfairTryAcquire(acquires);
}
// Sync类
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()) {
int nextc = c + acquires;
if (nextc < 0) // overflow
throw new Error("Maximum lock count exceeded");
setState(nextc);
return true;
}
return false;
}
------------------------------公平锁tryAcquire------------------------------
// FairSync类
protected final boolean tryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
if (c == 0) {
if (!hasQueuedPredecessors() &&
compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(current);
return true;
}
}
else if (current == getExclusiveOwnerThread()) {
int nextc = c + acquires;
if (nextc < 0)
throw new Error("Maximum lock count exceeded");
setState(nextc);
return true;
}
return false;
}
解锁:公平与非公平模式都是调Sync的tryRelease
// ReentrantLock类
public void unlock() {
sync.release(1);
}
// Sync类
protected final boolean tryRelease(int releases) {
int c = getState() - releases;
if (Thread.currentThread() != getExclusiveOwnerThread())
throw new IllegalMonitorStateException();
boolean free = false;
if (c == 0) {
free = true;
setExclusiveOwnerThread(null);
}
setState(c);
return free;
}
三、Semaphore
Semaphore类结构
Semaphore源码
加锁:区分公平和非公平两种模式
// Semaphore类
public void acquireUninterruptibly() {
sync.acquireShared(1);
}
------------------------------非公平锁tryAcquire------------------------------
// NonfairSync类
protected int tryAcquireShared(int acquires) {
return nonfairTryAcquireShared(acquires);
}
// Sync类
final int nonfairTryAcquireShared(int acquires) {
for (;;) {
int available = getState();
int remaining = available - acquires;
if (remaining < 0 ||
compareAndSetState(available, remaining))
return remaining;
}
}
------------------------------公平锁tryAcquire------------------------------
// FairSync类
protected int tryAcquireShared(int acquires) {
for (;;) {
if (hasQueuedPredecessors())
return -1;
int available = getState();
int remaining = available - acquires;
if (remaining < 0 ||
compareAndSetState(available, remaining))
return remaining;
}
}
解锁:公平与非公平模式都是调Sync的tryRelease
// Semaphore类
public void release(int permits) {
if (permits < 0) throw new IllegalArgumentException();
sync.releaseShared(permits);
}
// Sync类
protected final boolean tryReleaseShared(int releases) {
for (;;) {
int current = getState();
int next = current + releases;
if (next < current) // overflow
throw new Error("Maximum permit count exceeded");
if (compareAndSetState(current, next))
return true;
}
}
四、CountDownLatch
countDownLatch在加锁时,只看state是否为0,无关公平与否, 因此只有公平模式
CountDownLatch类结构
CountDownLatch源码
---------------------------------加锁---------------------------------
// CountDownLatch类
public void await() throws InterruptedException {
sync.acquireSharedInterruptibly(1);
}
// Sync类
protected int tryAcquireShared(int acquires) {
return (getState() == 0) ? 1 : -1;
}
---------------------------------解锁---------------------------------
// CountDownLatch类
public void countDown() {
sync.releaseShared(1);
}
// Sync类
protected boolean tryReleaseShared(int releases) {
// Decrement count; signal when transition to zero
for (;;) {
int c = getState();
if (c == 0)
return false;
int nextc = c - 1;
if (compareAndSetState(c, nextc))
return nextc == 0;
}
}