ReentrantLock.lock()
调用lock()函数
public void lock() {
sync.lock();
}
此处的sync.lock有两种实现,第一种是FairSync类中的lock(),第二种是NonfairSync类中的lock()
static final class FairSync extends Sync {
private static final long serialVersionUID = -3000897897090466540L;
final void lock() {
acquire(1);
}
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;
}
}
static final class NonfairSync extends Sync {
private static final long serialVersionUID = 7316153563782823691L;
/**
* Performs lock. Try immediate barge, backing up to normal
* acquire on failure.
*/
final void lock() {
if (compareAndSetState(0, 1))
setExclusiveOwnerThread(Thread.currentThread());
else
acquire(1);
}
protected final boolean tryAcquire(int acquires) {
return nonfairTryAcquire(acquires);
}
}
ReentrantLock会根据当前锁的类型,调用FairSync或者NonfairSync中的lock函数。此处以FairSync举例。
final void lock() {
acquire(1);
}
acquire()是AbstractQueuedSynchronizer类中的函数
public final void acquire(int arg) {
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
此处注意一点,当!tryAcquire(arg) && acquireQueued(addWaiter(Node.EXCLUSIVE), arg)为true时,该进程会中断 这是因为,
acquireQueued(addWaiter(Node.EXCLUSIVE), arg)
函数的表达式为true,也就是说,该进程,在等待的过程中,发生了中断。
parkAndCheckInterrupt()
函数将会返回被唤醒线程的中断状态,如果当前进程的中断状态为true,那么
shouldParkAfterFailedAcquire(p, node) && parkAndCheckInterrupt()
也为true,程序就会执行
interrupted = true;
这样一条语句。而interrupted恰好是
acquireQueued(addWaiter(Node.EXCLUSIVE), arg)
函数的返回值 。此时就会执行selfInterrupt()函数,中断自己。
static void selfInterrupt() {
Thread.currentThread().interrupt();
}
到此处为止,出现的class的关系
- ReentrantLock中有有个虚的静态类Sync,此类继承了AbstractQueuedSynchronizer。
- ReentrantLock中还有FairSync和NonfairSync,这两个类继承了Sync,而ReentrantLock通过构造函数决定此锁是公平或者不公平。
- 在FairSync中,函数会调用AbstractQueuedSynchronizer.acquire()函数,进行下一步的加锁行为。
进入acquire()函数中后,第一步会回调FairSync类中的tryAcquire函数,此函数用来尝试得到锁。
protected final boolean tryAcquire(int acquires) {
final Thread current = Thread.currentThread();
//获取当前进程的状态
int c = getState();
if (c == 0) {
//如果自己不用排队,那么直接cas操作尝试获得锁
if (!hasQueuedPredecessors() &&
compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(current);
return true;
}
}
//如果当前状态不为0,有两种情况
//第一种 当前进程等于拥有锁的进程,这也代表了这是一个可重入锁,此时c = c + acquires(这个值此时为1)
//第二种 当前进程不是拥有锁的进程,直接返回false
else if (current == getExclusiveOwnerThread()) {
int nextc = c + acquires;
if (nextc < 0)
throw new Error("Maximum lock count exceeded");
setState(nextc);
return true;
}
return false;
}
-
首先注意到一点 : acquire中的判断为!tryAcquire(arg) && acquireQueued(addWaiter(Node.EXCLUSIVE), arg) 也就说明一点,当此判断返回为false时,会将当前进程中断。在FairSync类中的tryAcquire函数中,如果获得锁,则返回true,没有获得锁,则返回false。 那么,当返回值为true时,!tryAcquire(arg)为false,此时,此判断直接为false,什么都不干,也即进程得到锁,继续执行。
-
当tryAcquire返回为false(尝试得到锁失败时),acquire中的判断继续执行。
-
注意!status和waitstatus需要区别清楚!
此处hasQueuedPredecessors()的判断极为复杂,先按下不表,现在只用知道hasQueuedPredecessors()是用来判断自己是否需要排队。
如果判断继续执行,则会执行acquireQueued(addWaiter(Node.EXCLUSIVE), arg)函数
final boolean acquireQueued(final Node node, int arg) {
boolean failed = true;
try {
//此处是一个无条件循环
boolean interrupted = false;
for (;;) {
//p为当前Node的前置节点
final Node p = node.predecessor();
//如果Node的前置节点为头节点,并且当前进程尝试得到锁成功,
//那么就说明此Node在等待队列中,为除头节点的第一个节点,
//并且此时已经得到了当前的锁,此进程就可以继续运行了
//如果得到锁失败,则表示此行为是一个自旋!!!!
//如果Node的前一个结点不是Head,就表示这个Node轮不到去抢锁(因为是公平锁)
//以上这两种情况都会park当前进程
if (p == head && tryAcquire(arg)) {
//setHead()将当前Node变为头节点,并且将Node中的thread = null
setHead(node);
//help GC
p.next = null; // help GC
failed = false;
return interrupted;
}
//如果上面的if语句没有进入,就表示你没有抢到锁,或者说是没有抢锁的资格
//那么很遗憾,你会被park;
//此处有一点需要特别注意,shouldParkAfterFailedAcquire函数用来设置Node的前一个Node的waitStatus
//如果前一个Node的ws为-1,则表示这个Node已经被更改过了,已经处于park的状态了,直接返回true
//如果前一个Node的ws大于0,则表示前一个Node是被废弃的,程序会跳过这一个Node,继续向前找Node
//如果前一个Node是其他情况,那么就表示ws==0,程序会将ws变成-1(注意此时是不会返回true的)
//为什么呢?因为前一个Node的ws如果是0,就表示前一个Node也就park了,但是他的ws还没有被修改。
//此时就需要当前Node来修改前一个Node的ws了,因为只有我们能够确定前一个Node被park时,我们才能将他的ws设置为-1
//为什么不让前一个Node自己做呢?因为在高并发的情况下,设置ws和park自己并不是线程安全的,
//所以我们需要让当前Node更改前一个Node的ws值
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
interrupted = true;
}
} finally {
if (failed)
cancelAcquire(node);
}
}
其中addWaiter的返回值是一个Node。这个Node也就是即将插入等待队列的一个Node。此函数用来对此Node进行初始化
private Node addWaiter(Node mode) {
Node node = new Node(Thread.currentThread(), mode);
// Try the fast path of enq; backup to full enq on failure
Node pred = tail;
if (pred != null) {
node.prev = pred;
if (compareAndSetTail(pred, node)) {
pred.next = node;
return node;
}
}
enq(node);
return node;
}
回头来看acquireQueued函数,此函数用来把Node加入等待队列中,在这个函数中,线程在阻塞进入队列前,会再次进行一次自旋,首先看自己是否有拿到锁的资格。(如果不是除头节点外第一个结点,没有资格拿锁。)再进行对锁的申请,如果没有抢占到锁,才会进队列并且阻塞。
tryAcquire()方法中的hasQueuedPredecessors()
此方法用来判断当前申请锁的线程是否需要排队
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;
}
public final boolean hasQueuedPredecessors() {
// The correctness of this depends on head being initialized
// before tail and on head.next being accurate if the current
// thread is first in queue.
Node t = tail; // Read fields in reverse initialization order
Node h = head;
Node s;
return h != t &&
((s = h.next) == null || s.thread != Thread.currentThread());
}
hasQueuedPredecessors()函数在当前进程需要排队时返回true,不需要排队时返回false。 首先来看第一个条件 h != t ,h != t表示等待队列是非空的。如果队列是空的,h != t为false,函数会直接把返回false。不为空则会进入第二个条件的判断。 第二个条件,((s = h.next) == null || s.thread != Thread.currentThread())。可以分成两部分,两部分中有一个为true则返回true。第一部分(s = h.next) == null,即h.next==null。这说明等待队列中只有头节点一个节点,没有其他节点(即没有进程正在等待)。此时函数返回true,表示需要排队。第二部分s.thread != Thread.currentThread(),表示第一部分判断为false,此时等待队列中有其他的进程正在等待。如果h.next.thread!=Thread.currentThread()则说明当前申请锁的进程不是等待队列中的第一个进程,这也说明在当前进程前仍然有进程比你等待,明明是它先来的,所以你得等着。当前进程仍然需要排队,所以函数返回true。反之则表示,你就是先来的,是第一个,所以不需要再次等待,函数返回false。