JDK1.8
前言
《JUC——ReentrantLock源码简读(上)》提到会在这篇讨论hasQueuedPredecessors()
方法,为了方便看,再来贴下源码和上篇写的注释:
package java.util.concurrent.locks;
public class ReentrantLock implements Lock, java.io.Serializable {
// ....忽略很多源码
/**
* Sync object for fair locks
*/
static final class FairSync extends Sync {
/**
* Fair version of tryAcquire. Don't grant access unless
* recursive call or no waiters or is first.
*/
/**
* tryAcquire()返回false,需要入队,有这几种情况:
* 1. 锁被其他线程持有,并且不是重入情况
* 2. 锁还没被其他线程持有,但是队列中还有其他等锁的线程
* 3. 锁还没被其他线程持有,队列中也没有其他线程等锁,但是CAS操作失败
*/
protected final boolean tryAcquire(int acquires) {
// 5.1 获取当前线程
final Thread current = Thread.currentThread();
/**
* 5.2 这个getState获取的是java.util.concurrent.locks.AbstractQueuedSynchronizer
* 类下的state变量的值,当有线程获取到锁时,这个值加1,
* 等于0表示当前还没有线程获取到锁
*/
int c = getState();
if (c == 0) {
/**
* 5.3 当前这个线程判断到state等于0,但是并不代表它就能获取到锁,因为
* 有可能队列中还有等锁的线程,只是这个线程来的时候,持有锁
* 的线程刚好释放锁而已。所以,hasQueuedPredecessors()方法就是
* 去判断是否要把当前线程加入队列
*
* 如果hasQueuedPredecessors()返回fale,则进行CAS操作
* 如果hasQueuedPredecessors()返回true,tryAcquire()返回false,进行入队操作
*/
if (!hasQueuedPredecessors() &&
compareAndSetState(0, acquires)) {
// 把当前线程设置为持有锁的线程
setExclusiveOwnerThread(current);
return true;
}
}
else if (current == getExclusiveOwnerThread()) {
/**
* 5.3^ 如果当前线程等于持有锁的线程,说明是线程重入锁,则把state值加1
* (ReentrantLock是可重入锁)
*/
int nextc = c + acquires;
if (nextc < 0)
throw new Error("Maximum lock count exceeded");
setState(nextc);
return true;
}
return false;
}
}
}
hasQueuedPredecessors()
方法源码:
public abstract class AbstractQueuedSynchronizer
extends AbstractOwnableSynchronizer
implements java.io.Serializable {
// ....忽略很多源码
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()
在公平锁tryAcquire()方法中,如果当前锁的状态是0
(即没有线程持有锁),当前线程首先会去执行hasQueuedPredecessors()
方法判断自己要不要排队,有好几种情况。
情况1
t1来了,队列还没有被初始化
h!=t -> false
整个方法返回false,可以去执行CAS操作
情况2
t1持有锁,t2加入等锁队列后执行acquireQueued(),由于它是第一个等锁线程,会进行自旋操作。t2第一次自旋之前,t1把锁释放了,队列中是这种情况:
t2执行自旋进入hasQueuedPredecessors()
方法:
h!=t -> true
s = t2
s == null -> false
s.thread != Thread.currentThread()) -> false
整个方法返回false,可以去执行CAS操作
情况3
t1持有锁,t2在队列中等锁,队列中是这种情况:
然后t1把锁释放了,把state改成0,thread改成null,但还没来记得及唤醒t2,或者t2被唤醒了,但是此时t3来了先去执行tryAcquire()进入hasQueuedPredecessors()
方法:
h != t -> true
s = t2
s == null -> false
s.thread != Thread.currentThread()) -> true
整个方法返回true,t3直接入队列,这就体现了公平锁,即使t3先进入到tryAcquire()方法中,但是还是拿不到锁,只能让给t2
情况4
t3拿到锁在执行,队列中是这种情况:
然后t3释放锁,把state改成0,thread改成null,此时t4来了,执行tryAcquire()进入hasQueuedPredecessors()
方法:
h != t -> false
整个方法返回false,t4可以执行CAS操作
情况5
假设此时队列的状态是t1拿到锁,t2进入队列(还没开始自旋),然后t1释放锁,
此时t3来了,执行tryAcquire()hasQueuedPredecessors()
方法:
h != t -> true
刚执行完上面那不,还没往后执行的,CPU时间片被t2抢去了,t2执行第一次自旋:
h != t -> true
s = t2
s == null -> false
s.thread != Thread.currentThread()) -> false
整个方法返回false,t2执行CAS操作成功后获取锁,并修改队列状态:
然后t3继续执行:
// h != t -> true 这是之前执行的
// 此时h变成了t2
s == null -> true
整个方法返回ture,t3入队。