ReentrantLock分为公平锁和非公平锁,也就是FairSync(公平同步器)和NonfairSync(非公平同步器)这两个同步器来实现公平锁和非公平锁
FairSync和和NonfairSync都是集成AQS这个抽象队列同步器,主要是实现了AQS的tryAcquire这个方法,而这个方法就是获取锁资源,当这个方法返回true时表示当前线程获取到锁了,如果返回false则表示当前线程没有获取到锁,需要将当前线程加到获取锁的等待队列里面去。而FairSync和和NonfairSync这两个同步器的tryAcquire方法实现方式是不一样的。
FairSync#tryAcquire实现方式:
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;
}
NonfairSync#tryAcquire实现方式:
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()) {
int nextc = c + acquires;
if (nextc < 0) // overflow
throw new Error("Maximum lock count exceeded");
setState(nextc);
return true;
}
return false;
}
可以看到两个同步器的tryAcquire的实现方式基本相同,唯一不同的地方就是FairSync同步器里面多了hasQueuedPredecessors这个方法,这个方法从名称上看就能理解其含义就是判断当前线程的节点前面有没有其他节点,如果有则直接返回false,表示这次没机会获取锁,这样当前线程直接就进入到获取锁的等待队列。而NonfairSync的实现方式少了hasQueuedPredecessors这个方法就说明任何一个新线程都可以直接去尝试获取锁,这就体现出了公平性和非公平性:公平性:先到先尝试获取锁;非公平性:都可以尝试获取锁。
AQS获取锁和释放锁的过程:
获取锁等待队列
head --->n2 --->n3--->tail
++++ ++++ ++++ ++++
锁始终由head持有,当head需要释放锁的时候,会唤醒head的下一个符合条件的节点n2,节点n2如果获取到锁则将自己设置为head。
head唤醒下一个符合条件节点的实习方式:
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;
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;
}
if (s != null)
LockSupport.unpark(s.thread);
}
也就是唤醒head的下一个节点,如果head下一个节点是空的或者下一个节点的状态>0,则接着找head的下一个节点的下一个节点,以此往后遍历直到找到符合条件的节点,只是寻找的方式是从尾部tail往前遍历寻找
当新的线程需要获取锁的时候,如果没有获取到锁,则创建一个节点与当前线程绑定,并将这个节点加入到获取锁的等待队列的尾部,并将自己设置为tail