是java 实现的公平锁/非公平锁,也是可重入锁
AbstractQueuedSynchronizer
翻译过来是抽象队列同步 是不是意味着aqs
内部维护中一个队列。
1.下图是 ReentrantLock
内部实现 公平锁FairSync
跟aqs
的关系
2.下图是 ReentrantLock
内部实现 公平锁NonfairSync
跟aqs
的关系
3.aqs
内部维护的队列是什么样子的
简单看下aqs
内部队列的实现
static final class Node {
//该节点的上一个节点
volatile Node prev;
//该节点的下一个节点
volatile Node next;
//持有该节点的线程
volatile Thread thread;
}
可以看出来aqs
内部维护着一个Node
有点像LinkedList
,所以aqs
底层准确来说是由一个链表实现的
4.aqs
出了有个内部类是Node
外,aqs
这个类属性还维护着链表的头节点和尾节点
//头节点
private transient volatile Node head;
//尾节点
private transient volatile Node tail;
先来一个逻辑图,ReentrantLock加锁的过程就是如此简单:
1、通过lock()方法加锁,通过unlock()方法释放锁;
2、调用lock()之后,内部会判断当前锁的状态,只有在state == 0
成立的情况下锁才是空闲的。
3、在空闲状态下,并不能立刻加锁成功,还要判断等待队列中有没有等待的线程。如果没有则直接加锁成功,如果有则优先从队列中获取线程加锁。
4、如果state > 0
则说明当前锁被其他线程持有,持有该锁的线程在exclusiveOwnerThread
属性中关联。
5、判断exclusiveOwnerThread == currentThread
是否成立;如果成立说明当前线程持有该锁,则对state累加1(通过这种方式实现锁重入);
6、如果该锁不是当前线程持有,则把该线程加入到队列中等待。
ReentrantLock加锁逻辑简单明了,下面则对实现源码分析。
首先,根据我们传入的boolean值决定ReentrantLock为公平锁还是非公平锁。(默认为非公平锁)
我们调用lock()方法加锁时,底层实际为调用ReentrantLock持有的Sync对象的acquire(1)方法。
public void lock() {
sync.acquire(1);
}
acquire(1)方法调用tryAcquire(1)方法尝试加锁,失败后调用acquiredQueued(addWaiter(Node.EXECUSIVE),arg)方法将当前线程包装成一个Node节点加入到等待队列
public final void acquire(int arg) {
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
tryAcquire()方法会首先判断锁是否空闲,如果空闲则用hasQueuePreProcessor()判断自己是否需要排队,如果不需要排队则CAS操作尝试获取锁,获取锁成功,则将持有锁的线程设置为自己。如果需要排队,则返回false,表示用tryAcquire(1)方法尝试获取锁失败,将自己加入到等待队列。
@ReservedStackAccess
protected final boolean tryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
//锁空闲
if (c == 0) {
//判断自己是否需要排队
if (!hasQueuedPredecessors() &&
compareAndSetState(0, acquires)) { //不需要排队则用CAS获取锁
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;//否则,返回尝试加锁失败
}
如果tryAcquire(1)判断锁非空闲,则看是否为自己持有锁,若为自己持有,则重入。否则返回false,表示尝试获取锁失败,将自己加入到等待队列。(该方法分公平锁实现和非公平锁两种实现)
//判断自己是否需要排队
public final boolean hasQueuedPredecessors() {
Node h, s;
//如果等待队列的head不为空
if ((h = head) != null) {
if ((s = h.next) == null || s.waitStatus > 0) {
s = null; // traverse in case of concurrent cancellation
for (Node p = tail; p != h && p != null; p = p.prev) {
if (p.waitStatus <= 0)
s = p;
}
}
//判断是否有等待着的线程且不是自己
if (s != null && s.thread != Thread.currentThread())
return true;
}
//如果head为null,则不需要等待,因为没有等待队列
return false;
}
acquiredQueued(addWaiter(Node.EXECUSIVE),arg)会首先执行addWaiter()方法,该方法比较简单。
private Node addWaiter(Node mode) {
Node node = new Node(mode);
//大体逻辑就是判断等待队列是否为空,如果是空,则初始化之后再将自己插入,否则直接将自己插入队尾
for (;;) {
Node oldTail = tail;
if (oldTail != null) {
node.setPrevRelaxed(oldTail);
if (compareAndSetTail(oldTail, node)) {
oldTail.next = node;
return node;
}
} else {
initializeSyncQueue();
}
}
}
然后再执行acquireQueued方法。首先判读自己是否是head的.next节点,如果是则tryAcquire(1)再次尝试获取锁,如果不是调用park()让自己睡眠。
final boolean acquireQueued(final Node node, int arg) {
boolean interrupted = false;
try {
for (;;) {
final Node p = node.predecessor();
if (p == head && tryAcquire(arg)) {
setHead(node);
p.next = null; // help GC
return interrupted;
}
if (shouldParkAfterFailedAcquire(p, node)) //此处会多自旋一次
//因为每个Node节点的waitStatus是系统默认值0,会返回false,自旋一次
//即每一次当前节点会把前一个节点的状态改为-1,即SIGNAL状态
//为啥不自己改自己呢?因为自己无法更改,如果在park前设置,但无法保证原子性
interrupted |= parkAndCheckInterrupt();
}
} catch (Throwable t) {
cancelAcquire(node);
if (interrupted)
selfInterrupt();
throw t;
}
}
非公平锁和公平锁加锁过程最大的不同是tryAcquire(1)方法,非公平锁该方法的实现是调用了nonfairTryAcquire(acquires)。
即lock()—>Sync.acquire(1)---->tryAcquire(1)---->nonfairTryAcquire(acquires)。
@ReservedStackAccess
final boolean nonfairTryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
//非公平锁只要判断锁是空闲的,则直接尝试CAS获取锁,而不会判断自己是否需要排队
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(1)获取锁失败,则后面的流程和公平锁一样。