多线程学习--关于ReentrantLock源码详细解读
对于源码的阅读,有几点总结:
1.跑不通的源码,不读;
2.解决问题为目的;
3.总体把握,先不扣细节;
4.抓住一条线,往下阅读
话不多说,简单demo断点调试,JDK版本为1.8
public class TestReentrantLock {
private static volatile int i = 0;
public static void main(String[] args) {
ReentrantLock lock = new ReentrantLock();
lock.lock(); //在此打断点
i++;
lock.unlock();
}
}
首先,关于重要方法的类的继承关系图如下
整体方法调用流程如下图
由lock.lock()方法进入到ReentrantLock类中,得到lock()方法的实现其实是调用了sync.lock();此方法是由Sync的子类NonfairSync实现的。源码如下
final void lock() {
if (compareAndSetState(0, 1))
//如果抢到锁,设置为私有
setExclusiveOwnerThread(Thread.currentThread());
else
//没抢到锁,进入等待队列
acquire(1);
}
通过CAS来获取锁,如果抢到锁,就设为私有,由此可见ReentrantLock是排他锁;
如果没抢到,则进入等待队列;我们来继续看看acquire()方法,源码如下
//tryAcquire(arg):
//acquireQueued(addWaiter(Node.EXCLUSIVE), arg)
//addWaiter()
//没抢到锁,并且加入到等待队列的尾部
if (!tryAcquire(arg) &&acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
其中条件中有三个方法,依次为tryAcquire()、acquireQueued()和addWaiter();
1.先看tryAcquire()方法,见名知意尝试获得,尝试去获得锁。它是通过ReentrantLock类中的内部类NofairSync实现的;调用了nonfairTryAcquire()方法;源码如下
final boolean nonfairTryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
if (c == 0) { //如果没加锁
if (compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(current); //抢到锁,设置私有
return true;
}
}
//有锁,是自己线程的,重入,c+acquires
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;
}
由源码可知,如果是第一次获得锁,那就设为私有;如果此线程已经抢到锁,那么就将state状态修改,c+acquires,即+1,由此可见ReentrantLock是可重入锁
2.再看addWaiter()方法,关键,源码如下
private Node addWaiter(Node mode) {
//node为当前线程
Node node = new Node(Thread.currentThread(), mode);
Node pred = tail;
if (pred != null) {
node.prev = pred;
if (compareAndSetTail(pred, node)) {
pred.next = node;
return node;
}
}
enq(node);
return node;
}
prev:代表前一个节点
next:代表后一个节点
tail:代表尾部节点
由if (!tryAcquire(arg) &&acquireQueued(addWaiter(Node.EXCLUSIVE), arg))理解,如果!tryAcquire(arg)为true,即没获取到锁,那么将进入等待队列。
addWaiter()即将此节点加入到等待队列的尾部;node为当前节点,将原队列的尾部节点赋值给pred节点,如果不为空,则将node节点的前一个节点指向pred(即原队列的尾部节点)
然后进行CAS进行设置,如果没有其他节点加入进来,那么此节点加入成功,pred(即原尾部节点)的下一个节点指向node,即将node加入到了原队列的尾部
3.acquireQueued()方法,获得等待队列;源码如下
final boolean acquireQueued(final Node node, int arg) {
boolean failed = true;
try {
boolean interrupted = false;
for (;;) {
final Node p = node.predecessor(); //node的前任节点
if (p == head && tryAcquire(arg)) { //前任节点是头节点,并且得到锁
setHead(node); //将node设置为头节点
p.next = null; // help GC
failed = false;
return interrupted;
}
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
interrupted = true;
}
} finally {
if (failed)
cancelAcquire(node);
}
}
shouldParkAfterFailedAcquire()方法源码如下:
private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
int ws = pred.waitStatus;
if (ws == Node.SIGNAL)
return true;
if (ws > 0) {
do {
node.prev = pred = pred.prev;
} while (pred.waitStatus > 0);
pred.next = node;
} else {
compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
}
return false;
}
parkAndCheckInterrupt()方法源码如下:
private final boolean parkAndCheckInterrupt() {
LockSupport.park(this);
return Thread.interrupted();
}
关于ReentrantLock源码的简单理解,希望各路大神多多指点。
本文地址:https://blog.csdn.net/mrcool2012/article/details/109851063