- AQS重要性:
Java ---> JVM
JUC ---> AQS
主要用于解决锁分配给谁的问题。
整体就是一个抽象的FIFO队列来完成资源获取线程的排队工作,并通过一个int类变量表示持有锁的状态。
和AQS有关的
进一步理解锁和同步器的关系
锁,面向锁的使用者
同步器,面向锁的实现者
AQS源码说明
int 默认值0
AQS同步队列的基本结构
AQS内部体系架构
“银行候客区的顾客”
1. AQS源码分析
ReentrantLock,从最简单的lock方法看公平和非公平
公平锁 vs 非公平锁
公平锁 vs 非公平锁 ----- 唯一的区别
hasQueuedPredecessors判断了是否需要排队
非公平锁:“不讲武德”,一来就抢
acquire() ---- 用了 模板方法设计模式
尝试获取锁,获取不到,排队(阻塞当前线程)
1)tryAcquire()
tryAcquire 方法,尝试获取锁。以下几种情况,会导致获取锁失败:
- 锁已经被其他线程获取;
- 锁没有被其他线程获取,但当前线程需要排队;
- cas失败(可能过程中已经有其他线程拿到锁了)
锁为自由状态(c == 0),并不能说明可以立即执行cas获取锁,因为可能在当前线程获取锁之前,已经有其他线程在排队了,必须遵循先来后到原则获取锁。所以还要调用hasQueuedProdecessors方法,查看自己是否需要排队。
/**
* Fair version of tryAcquire. Don't grant access unless
* recursive call or no waiters or is first.
*/
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;
}
2)addWaiter()
尝试获取锁失败的情况下,将当前线程封装成Node对象,并加入到排队队列中。
enq()
没有其他线程在排队,调用enq构造队列,并将node加入到队列。
waitStatus
顾客A在办业务;
顾客B去排队:
head, tail
- new Node() 哨兵节点,虚拟节点,占位 Thread = null, waitStatus = 0
- 节点B,Thread = 顾客B,waitStatus = 0
3)acquireQueued()
整个aqs的核心和难点之一。
注意这里使用了for(;;)
判断node的前辈节点是不是head
shouldParkAfterFailedAcquire
parkAndCheckInterrupt()
用的LockSupport.park()
当前节点把前驱节点的waitStatus更新为-1
4)unlock() --> sync.release(1) --> tryRelease() --> unparkSuccessor()
release()
tryRelease()
unparkSuccessor()
unpark后,acquireQueued大循环还没完呢。。。
5)cancelAcquire()
异常情况 if(failed)
/**
* Cancels an ongoing attempt to acquire.
*
* @param node the node
*/
private void cancelAcquire(Node node) {
// Ignore if node doesn't exist
if (node == null)
return;
node.thread = null;
// Skip cancelled predecessors
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 = Node.CANCELLED;
// If we are the tail, remove ourselves.
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;
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 {
unparkSuccessor(node);
}
node.next = node; // help GC
}
}
2. AQS 总结
整个ReentrantLock的加锁过程,可以分为三个阶段:
1)尝试加锁;
2)加锁失败,线程入队列;
3)线程入队列后,进入阻塞状态。