ReentrantLock 之lock unLock源码分析,基于非公平锁
Node需要了解的属性和它的功能
waitStatus这个字段解释: 这个字段其实是表示它的next节点的一个状态(初始化默认0,其他取值范围有CANCELLED、SIGNAL、CONDITION,意义见下)
-AbstractQueuedSynchronizer
-Node
SHARED = new Node() //表示时共享锁的
EXCLUSIVE = null //表示时独占的
CANCELLED = 1 //被取消的节点
SIGNAL = -1 //表示Node对象 在等待队列里
CONDITION = -2 //表示Node对象 在条件队列里
PROPAGATE //
waitStatus //该节点现在的状态 取值范围CANCELLED、SIGNAL、CONDITION
prev //指向前一个Node节点
next //指向下一个Node节点
thread //当前的线程
nextWaiter //条件队列里下一个等待节点
lock() 详解
//sync类有2个实现类,公平和非公平锁
//加锁入口
public void lock() {
sync.lock();
}
//这是非公平锁实现的sync.lock();
final void lock() {
//compareAndSetState(0, 1) Unsafe类直接操作内存改变AQS类中的state字段的值,修改成功则表示加锁成功
if (compareAndSetState(0, 1))
//加锁成功后将当前线程放入到AQS类继承AbstractOwnableSynchronizer类中exclusiveOwnerThread字段
setExclusiveOwnerThread(Thread.currentThread());
else
acquire(1);
}
//再次尝试去加锁acquire(1);
public final void acquire(int arg) {
//tryAcquire(arg) 再次尝试加锁,这个尝试方法由公平和非公平锁自己实现
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg)) //Node.EXCLUSIVE这个值再独占锁中是NULL
selfInterrupt();//如果进了这里,表示该线程已结束了,或者请求取消了
}
//tryAcquire(arg) 接第18行代代码,参数acquires的值为1
protected final boolean tryAcquire(int acquires) {
return nonfairTryAcquire(acquires);
}
//接第24行代码,参数acquires的值为1;该方法功能是,尝试加锁和判断是否是重入锁的操作
final boolean nonfairTryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
//如果为0,表示锁是释放状态
if (c == 0) {
//尝试再次加锁
if (compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(current);
return true;
}
}
//表示当前锁已被占用,这里的if是判断当前线程和占有锁的线程是否是同一个线程
else if (current == getExclusiveOwnerThread()) {
//将状态加1,这是可重入锁,锁lock锁是需要加几次锁就要释放几次锁
int nextc = c + acquires;
if (nextc < 0) // overflow
throw new Error("Maximum lock count exceeded");
setState(nextc);
return true;
}
//尝试抢锁失败
return false;
}
//目光回到第19行代码addWaiter(Node.EXCLUSIVE)方法
//上面抢锁失败,方法作用:将该线程封装,加入等待队列。参数mode为独占锁默认的NULL值
private Node addWaiter(Node mode) {
//创建有一个Node节点,放入当前线程,mode这个参数在独占锁必为NULL,这个构造中将mode赋值给了nextWait,就是指向下一个等待节点
Node node = new Node(Thread.currentThread(), mode);
// 获取当前AQS类中的位节点
Node pred = tail;
//如果尾节点不为空,表示该等待队列已经建立好了
if (pred != null) {
//将当前新Node对象的前指针指向当前AQS类中尾节点
node.prev = pred;
//compareAndSetTail(pred, node) CAS方式将之前的尾节点变成当前新创建的Node节点
if (compareAndSetTail(pred, node)) {
pred.next = node;
return node;
}
}
//走到这里,表示还未创建等待队列
enq(node);
return node;
}
//接68行,方法作用:初始化等待队列。参数node为当前线程的Node对象
private Node enq(final Node node) {
//自旋创建等待队列的头结点
for (;;) {
//获取当前AQS的尾节点
Node t = tail;
if (t == null) {
// CAS方式创建AQS中head节点,就是队列的头结点
if (compareAndSetHead(new Node()))
//并且将尾节点的指针也指向头结点
tail = head;
} else {
//表示将包含当前线程的Node对象中的前指针指向79行new出来的头节点
node.prev = t;
//尝试将AQS的尾指针换成当前线程的Node对象,这里换不成功,会一直自旋,直到换成功为止
if (compareAndSetTail(t, node)) {
t.next = node;//将上一个node对象的下节点指向新创建的node
return t;
}
}
}
}
//接19行,作用:尝试申请入队列。参数node为当前线程的封装类,arg为1
//这个方法时lock()最终执行方法,线程没获取到锁,就会在这个方法里面挂起来
final boolean acquireQueued(final Node node, int arg) {
boolean failed = true;
try {
boolean interrupted = false;
for (;;) { //当该线程为获取到锁时,就会阻塞在118行那个方法,当其他线程释放锁后且该线程为head的下有一个Node节点,那么这个自旋又会开始
//获取当前线程node对象的前节点
final Node p = node.predecessor();
//判断当前node对象是否是head的next节点,如果是就会再次去抢一下锁
if (p == head && tryAcquire(arg)) {
//抢锁成功后,将head设置为当前线程的node,setHead中会将前节点和Thread置为null
setHead(node);
p.next = null; // help GC
failed = false;
return interrupted;
}
//抢锁不成功或者前节点不为head节点时
/*
* shouldParkAfterFailedAcquire(p, node)新node节点调用此方法,第一次必定返回false,所以
* 必定还会走一次for循环,这次for循环又给了这个Node对象去抢锁的机会,所以新Node节点会有2此重复抢锁的机会;
*第二次for循环时shouldParkAfterFailedAcquire(p, node)返回值就是true了,接下来执行parkAndCheckInterrupt()方法;
*
*/
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
interrupted = true;
}
} finally {
if (failed)
cancelAcquire(node);
}
}
//接117行,作用:查看当前线程的前节点中的waitStatus的状态是否为-1(-1表示next节点为等待队列的节点,-2表示next节点为条件队列的节点)
private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
int ws = pred.waitStatus;
//新创建的Node对象第一次进来时,它的头节点必定为0
if (ws == Node.SIGNAL)//这个值是Node.SIGNAL = -1
return true;
if (ws > 0) {
do {
node.prev = pred = pred.prev;
} while (pred.waitStatus > 0);
pred.next = node;
} else {
//不为-1时,就将当前Node的前节点设置为 -1
compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
}
//第一次进来时,返回值为false,
return false;
}
//接118行,作用:将该线程设置成等待线程,调用了java源语Unsafe的park方法
private final boolean parkAndCheckInterrupt() {
LockSupport.park(this);
return Thread.interrupted();
}
unLock()源码分析
public void unlock() {
//为什么是传1,因为unLock操作只会释放一次加锁,所以前面说的调用了几次lock方法,就需要同样调几次unLock方法
sync.release(1);
}
//子类都是调用的AQS父类的release方法
public final boolean release(int arg) {
//这个tryRelease方法是ReentrantLock自己实现的
if (tryRelease(arg)) {
Node h = head;//获取当前的head节点
if (h != null && h.waitStatus != 0)//判断head的下一个节点是否需要被唤醒,这里waitStatus永远表示的都是next节点的状态,参照Node类的定义
unparkSuccessor(h);//准备去唤醒线程
return true;
}
return false;
}
//接8行,tryRelease方法是ReentrantLock自己实现的。尝试释放锁
protected final boolean tryRelease(int releases) {
int c = getState() - releases;//从22行判断可以看出,c必须==0才能成功释放锁
if (Thread.currentThread() != getExclusiveOwnerThread())//判断释放锁的线程是不是当前占有锁的线程
throw new IllegalMonitorStateException();
boolean free = false;
if (c == 0) {
free = true;
setExclusiveOwnerThread(null);//将当前占有锁的线程清空
}
setState(c);//将锁状态置为0
return free;
}
//接11行,AQS类的方法,方法作用:主要是去唤醒一个线程;参数node表示当前等待队列的head节点
private void unparkSuccessor(Node node) {
int ws = node.waitStatus;
if (ws < 0)
//每次有线程释放锁的时候,都会去把头结点的waitStatus设置为0;当有线程去加锁的时候,会判断这个waitStatus是否为-1,不为-1时,抢锁的线程就会自旋设置该值为-1
compareAndSetWaitStatus(node, ws, 0);
Node s = node.next;//该线程释放锁后,准备去唤醒head节点的next节点
//如果head节点没有next节点了,或者next节点的waitStatus>0(表示该线程已经被取消了)
if (s == null || s.waitStatus > 0) {
s = null;
//这里是从尾指针往前找,找一个waitStatus <= 0的线程,并且是离头部最近的线程,将这个线程拿去作为唤醒的对象
for (Node t = tail; t != null && t != node; t = t.prev)
if (t.waitStatus <= 0)
s = t;
}
//如果36行代码的s不为空,那么久会去尝试唤醒next节点,这里唤醒后,就会去执行上面讲解的lock()源码的99行代码的自旋方法(线程是被阻塞在lock源码讲解的118行那个方法)
if (s != null)
LockSupport.unpark(s.thread);
}
公平锁与非公平锁之间的区别
非公平锁tryAcquire的源码
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源码
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;
}
它俩的区别就在于公平锁中的第5行的那个方法
//返回true则表示该线程前面还有线程在等待,就不会去抢锁
public final boolean hasQueuedPredecessors() {
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());
}