Java并发编程之ReentrantLock源码(一)
前言
ReentrantLock
是基于AQS
实现的可重入锁,并且能够实现公平锁和非公平锁。
如何使用
public class Test4 {
private static ReentrantLock lock = new ReentrantLock();
public static void main(String[] args) {
lock.lock();
try {
System.out.println("获取锁...");
} finally {
lock.unlock();
}
}
}
- 创建一个
ReentrantLock
锁对象 - 调用
lock()
方法进行加锁 - 执行业务操作
- 在
finally
代码块中调用unlock()
方法保证一定能释放锁
源码解析
ReentrantLock
定义了一个抽象静态内部类Sync
继承自AbstractQueuedSynchronizer
,并且有两个实现类NonfairSync
和FairSync
,分别是非公平锁和公平锁实现。我们这里以非公平锁为例来介绍其源码实现流程。
abstract static class Sync extends AbstractQueuedSynchronizer
static final class NonfairSync extends Sync
static final class FairSync extends Sync
加锁成功
// NonfairSync加锁方法实现
final void lock() {
if (compareAndSetState(0, 1))
setExclusiveOwnerThread(Thread.currentThread());
else
acquire(1);
}
调用ReentrantLock
的lock
方法进行加锁,实际调用的是NonfairSync
的lock
方法,可以看到通过CAS
方式来修改state
的值,如果成功了说明加锁就成功了,然后把锁的拥有者标记为当前线程。
加锁失败
如果CAS
操作失败,那么就会调用AQS
中的acquire
方法继续尝试获取锁
public final void acquire(int arg) {
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
这里的if
语句分为两部分,第一部分tryAcquire
方法是在NonfairSync
类中,然后调用了Sync
类中的nonfairTryAcquire
方法,代码如下
// NonfairSync类
protected final boolean tryAcquire(int acquires) {
return nonfairTryAcquire(acquires);
}
// Sync类
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;
}
上面方法的作用就是再尝试一次看能不能获取到锁,首先获取state
值如果是0
说明当前没有加锁,所以尝试通过CAS
修改state
加锁,如果成功就把锁的拥有者线程标记为当前线程然后返回;如果state
不为0
说明有线程已经加锁,然后判断持有锁的线程是不是当前线程,如果是的话对state
加1
表示一次锁重入,然后返回即可。
再回到acquire
方法,如果tryAcquire
失败那么会判断第二个条件acquireQueued(addWaiter(Node.EXCLUSIVE), arg)
我们先看下addWaiter
方法做了哪些事
// AQS类
private Node addWaiter(Node mode) {
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;
}
首先根据当前线程和独占锁模式创建了一个node
,if
代码块的意思是,如果队列尾节点不为空,就尝试把新建的节点插入到尾结点的后面变成新的尾结点。
如果队列中是空的,就会执行enq
方法如下:
private Node enq(final Node node) {
for (;;) {
Node t = tail;
if (t == null) { // Must initialize
if (compareAndSetHead(new Node()))
tail = head;
} else {
node.prev = t;
if (compareAndSetTail(t, node)) {
t.next = node;
return t;
}
}
}
}
这里是一个for
循环会一直重试,首先判断尾节点是不是空,如果是空的说明队列就是空的,所以要进行初始化创建一个空节点同时作为头节点和尾节点。下次循环进入else
代码块,尝试把当前线程的节点添加到队尾,成功了就直接返回,失败了就会一直重试直到成功为止。
综上所述,
addWaiter
方法的作用就是根据当前线程和共享模式创建一个节点,然后添加到队列的尾部。
下一篇文章继续来看acquireQueued(addWaiter(Node.EXCLUSIVE), arg)
方法
转载自:https://juejin.cn/post/7228943838764154935