概述
在 jdk 中内置了可重入锁synchronized来维护代码的同步,但是synchronized是在虚拟机层面实现,并且功能比较少,难以满足一些特殊的情况,所以在concurrency包中就有了类似的方法ReentrantLock。今天我们就一起来分析下ReentrantLock的源码实现。
基本结构
实际上我们仔细阅读源码后发现ReentrantLock的实现很简单。他实现了接口Lock,但是他的同步实现实际上是在内部声明了一个叫Sync的类来实现的,而Sync是继承自同步器的核心类AbstractQueuedSynchronizer。
Sync
实际上ReentrantLock的核心全部在Sync之上,所以我们重点来研究它。
Sync是一个声明在ReentrantLock内部的抽象类,他继承了AQS,然后内部有两个类继承了Sync,一个是公平锁FairSync,一个是非公平锁NonfairSync。这两个类对应了两个锁的不同实现。
Sync 实现
Sync 的内部方法如下
abstract static class Sync extends AbstractQueuedSynchronizer { private static final long serialVersionUID = -5179523762034025860L;
abstract void lock();
final boolean nonfairTryAcquire(int acquires) {}
protected final boolean tryRelease(int releases) {}
protected final boolean isHeldExclusively() {}
final ConditionObject newCondition() {}
final Thread getOwner() {}
final int getHoldCount() {}
final boolean isLocked() {}
private void readObject(java.io.ObjectInputStream s) throws java.io.IOException, ClassNotFoundException {} }
复制代码
上述代码我们可以看到 Sync 内部除了实现了 AQS 的一些基本的方法之外有两个非常重要的方法,一个是tryRelease,另一个是nonfairTryAcquire。
tryRelease:
protected final boolean tryRelease(int releases) { // 获取当前state的值,并获取减去releases的数值之后的值 int c = getState() - releases; // 如果当前线程不是独占模式的所有者,那就抛出异常 if (Thread.currentThread() != getExclusiveOwnerThread()) throw new IllegalMonitorStateException(); boolean free = false; if (c == 0) { // 如果c值为0,那就说明锁已经可以释放 free = true; // 设置独占模式的所有者为空 setExclusiveOwnerThread(null); } // 将c的修改值存回去 setState(c); return free; }
复制代码
代码不难懂,主要是这个releases和state的值代表的意思是什么。state是一个代表锁状态的值,如果为 0,则说明锁是空闲可以被获取的,如果大于 0 则说明锁正在被占用。ReentrantLock是可重入锁就体现在此,每一次获取锁,state 的值便会加 1,释放的时候也是可以按照需求批量释放,直到 state 的值变成 0,则说明锁完全释放。
nonfairTryAcquire:
final boolean nonfairTryAcquire(int acquires) { // 获取当前线程 final Thread current = Thread.currentThread(); int c = getState(); // 如果state为0,即当前的锁是空闲状态 if (c == 0) { // 尝试获取锁 if (compareAndSetState(0, acquires)) { // 如果获取成功则设置独占线程 setExclusiveOwnerThread(current); return true; } } // 如果锁不空闲,但是当前线程就是独占的线程,那么依然可以获取这个锁 else if (current == getExclusiveOwnerThread()) { // 将state加上acquires的值 int nextc = c + acquires; // 如果nextc小于0,说明此时锁的计数溢出了,抛出异常 if (nextc < 0) throw new Error("Maximum lock count exceeded"); // 将c的修改值存回去 setState(nextc); return true; } return false; }
复制代码
依然是清晰明了的代码,我们可以很清楚的看到当发现当前线程是持有锁的线程时我们可以反复多次的获取锁,只需要将state加上相应的值便可。这个方法是非公平获取锁,非公平体现在第一次判断上,如果是公平锁的话讲究先来后到,如果发现锁不空闲,应该放到队列中等待,但是他在发现锁不为空闲的时候,首先尝试了一次获取锁(尝试插队),发现获取失败才会老老实实在队列中等待。
NonfairSync 实现
static final class NonfairSync extends Sync { private static final long serialVersionUID = 7316153563782823691L;
// ReentrantLock核心方法加锁 final void lock() { // 非公平的方式,首先尝试CAS改变state的值,成功则获取锁,失败在尝试公平获取 if (compareAndSetState(0, 1)) setExclusiveOwnerThread(Thread.currentThread()); else acquire(1); }
// 调用父类中的非公平锁实现 protected final boolean tryAcquire(int acquires) { return nonfairTryAcquire(acquires); } }
复制代码
FairSync 实现
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; } }
复制代码
总结
ReentrantLock的实现非常的简单,因为同步器的核心实现是在AQS中的,所以在ReentrantLock中我们能够涉及到的无非就是可重入锁的实现以及公平锁非公平锁的区分。
评论