Java 并发编程解析 | 根据 JDK 源码解析 Java 领域中 ReentrantLock 锁的规划思维与完成原理
最近我从cnaaa.com购买了云服务器。
在 Java 领域中,我们可以将锁大致分为根据 Java 语法层面 (关键词) 完成的锁和根据 JDK 层面完成的锁。
在 Java 领域中,尤其是在并发编程领域,关于多线程并发执行一直有两大中心问题:同步和互斥。其间:
- 互斥 (Mutual Exclusion):一个公共资源同一时间只能被一个进程或线程运用,多个进程或线程不能一起运用公共资源。即便是同一时间只允许一个线程拜访同享资源的问题。
- 同步 (Synchronization):两个或两个以上的进程或线程在运行过程中协同步调,按预订的先后次序运行。即便是线程之间怎么通信、协作的问题。
针对关于这两大中心问题,运用管程是可以处理和完成的,因而可以说,管程是并发编程的万能钥匙。
尽管,Java 在根据语法层面 (synchronized 关键字) 完成了对管程技术,可是从运用办法和性能上来说,内置锁 (synchronized 关键字) 的粒度相对过大,不支撑超时和中止等问题。
为了弥补这些问题,从 JDK 层面临其 “重复造轮子”,在 JDK 内部对其重新规划和界说,甚至完成了新的特性。
在 Java 领域中,从 JDK 源码剖析来看,根据 JDK 层面完成的锁大致首要可以分为以下 4 种办法:
- 根据 Lock 接口完成的锁:JDK1.5 版别供给的 ReentrantLock 类
- 根据 ReadWriteLock 接口完成的锁:JDK1.5 版别供给的 ReentrantReadWriteLock 类
- 根据 AQS 根底同步器完成的锁:JDK1.5 版别供给的并发相关的同步器 Semaphore,CyclicBarrier 以及 CountDownLatch 等�
- 根据自界说 API 操作完成的锁:JDK1.8 版别中供给的 StampedLock 类
从阅读源码不难发现,在 Java SDK 并发包首要经过 AbstractQueuedSynchronizer (AQS) 完成多线程同步机制的封装与界说,而经过 Lock 和 Condition 两个接口来完成管程,其间 Lock 用于处理互斥问题,Condition 用于处理同步问题。
一.AQS 根底同步器根本理论
在 Java 领域中,同步器是专门为多线程并发规划的同步机制,首要是多线程并发执行时线程之间经过某种同享状况来完成同步,只要当状况满意这种条件时线程才往下执行的一种同步机制。
一个标准的 AQS 同步器首要有同步状况机制,等候行列,条件行列,独占形式,同享形式等五大中心要素组成。
在 Java 领域中,JDK 的 JUC (java.util.concurrent.) 包中供给了各种并发东西,可是大部分同步东西的完成根据 AbstractQueuedSynchronizer 类完成,其内部结构首要如下:
- 同步状况机制 (Synchronization Status):首要用于完成锁 (Lock) 机制,是指同步状况,其要求关于状况的更新有必要原子性的
- 等候行列 (Wait Queue):首要用于寄存等候线程获取到的锁资源,而且把线程保护到一个 Node (节点) 里面和保护一个非堵塞的 CHL Node FIFO (先进先出) 行列,首要是选用自旋锁 + CAS 操作来确保节点刺进和移除的原子性操作。
- 条件行列 (Condition Queue):用于完成锁的条件机制,一般首要是指替换 “等候 – 通知” 作业机制,首要是经过 ConditionObject 目标完成 Condition 接口供给的办法完成。
- 独占形式 (Exclusive Mode):首要用于完成独占锁,首要是根据静态内部类 Node 的常量标志 EXCLUSIVE 来标识该节点是独占形式
- 同享形式 (Shared Mode):首要用于完成同享锁,首要是根据静态内部类 Node 的常量标志 SHARED 来标识该节点是同享形式
我们可以得到一个比较通用的并发同步东西根底模型,大致包含如下几个内容,其间:
- 条件变量 (Conditional Variable): 运用线程间同享的变量进行同步的一种作业机制
- 同享变量 ((Shared Variable)):一般指目标实体目标的成员变量和属性
- 堵塞行列 (Blocking Queue):同享变量 (Shared Variable) 及其对同享变量的操作共同封装
- 等候行列 (Wait Queue):每个条件变量都对应有一个等候行列 (Wait Queue), 内部需求完成入队操作 (Enqueue) 和出队操作 (Dequeue) 办法
- 变量状况描绘机 (Synchronization Status):描绘条件变量和同享变量之间状况改变,又可以称其为同步状况
- 作业形式 (Operation Mode): 线程资源具有排他性,因而界说独占形式和同享形式两种作业形式
综上所述,条件变量和等候行列的作用是处理线程之间的同步问题;同享变量与堵塞行列的作用是处理线程之间的互斥问题。
二. JDK 显式锁共同概念模型
在并发编程领域,有两大中心问题:一个是互斥,即同一时间只允许一个线程拜访同享资源;另一个是同步,即线程之间怎么通信、协作。
归纳 Java 领域中的并发锁的各种完成与运用剖析来看,一把锁或许一种锁,根本上都会包含以下几个方面:
- 锁的同步器作业机制:首要是考虑同享形式仍是独享形式,是否支撑超时机制,以及是否支撑超时机制?
- 锁的同步器作业形式:首要是根据 AQS 根底同步器封装内部同步器,是否考虑公正 / 非公正形式?
- 锁的状况变量机制: 首要锁的状况设置,是否同享状况变量?
- 锁的行列封装界说:首要是指等候行列和条件行列,是否需求条件行列或许等候行列界说?
- 锁的底层完成操作: 首要是指底层 CL 锁和 CAS 操作,是否需求考虑自旋锁或许 CAS 操作实例目标办法?
- 锁的组合完成新锁: 首要是根据独占锁和同享锁,是否考虑对应 API 自界说操作完成?
综上所述,大致可以根据上述这些方向,我们便可以清楚️知道 Java 领域中各种锁完成的根本理论时和完成思维。
三.ReentrantLock (可重入锁) 的规划与完成
在 Java 领域中,ReentrantLock (可重入锁) 是针关于 Java 多线程并发操控中对一个线程可以屡次对某个锁进行加锁操作,首要是根据内置的 AQS 根底笼统行列同步器完成的一种并发操控东西类。
一般来说,关于同一个线程是否可以重复占有同一个锁目标的视点来分,大致首要可以分为可重入锁与不行重入锁。其间:
- 可重入锁:一个线程可以屡次抢占同一个锁,也就意味着可以支撑一个线程对资源的重复加锁,或许说,一个线程可以屡次进入同一个锁所同步的临界区代码块。
- 不行重入锁:一个线程只能抢占一次同一个锁,也就意味着在同一时间只能有一个线程获取到锁,而其他获取锁的线程只能等候,只要具有锁的线程开释了锁后,其他的线程才可以获取锁。
ReentrantLock 是 JDK 中显式锁一个首要根据 Lock 接口 API 完成的根底完成类,具有与内置锁 (synchronized) 相同的并发性和内存语义,一起供给了限时抢占、可中止抢占等一些高级锁特性。
除此之外,ReentrantLock 根据内置的 AQS 根底笼统行列同步器完成,在线程参与锁资源竞赛比较激烈的场景下,能表现出比内置锁较佳的性能。
而且,ReentrantLock 是一种独占锁,在独占形式下只能逐一运用锁,也便是说,恣意时间最多只会有一个线程持有锁的操控权。
1. 规划思维
ReentrantLock 类最早是在 JDK1.5 版别供给的,从规划思维上来看,首要包含同步器作业形式,获取锁办法,开释锁办法以及界说 Condition 行列办法等 4 个中心要素。其间:
- 完成 Lock 接口 :首要根据 Lock 接口 API 完成对应办法,具有与内置锁 (synchronized) 相同的并发性和内存语义,用于支撑和处理处理互斥问题。
- 同步器作业形式:根据 AQS 根底笼统行列同步器封装内置完成一个静态的内置同步器笼统类,然后根据这个笼统类别离完成了公正同步器和非公正同步器,用来指定和描绘同步器作业形式是公正形式仍是非公正形式。
- 公正 / 非公正形式:首要描绘的是多个线程在一起获取锁时是否依照先到先得的次序获取锁,假如是则为公正形式,否则为非公正形式。
- 获取锁办法:首要界说了一个 lock () 办法来获取锁,表明假如锁现已被其他线程占有或持有,其当时获取锁的线程则进入等候状况。
- 开释锁办法:首要界说了一个 unlock () 办法来开释锁,表明假如锁现已被其他线程抛弃或开释,其当时获取锁的线程则获得该锁。
- 界说 Condition 行列操作办法: 首要是根据 Condition 接口来界说一个办法完成锁的条件机制,用于支撑线程的堵塞和唤醒功用即便是处理同步问题,也便是我们说的线程间的通信办法。
- 界说等候行列操作办法: 首要是根据条件行列来时进行对应的操作,直接适配 AQS 根底同步器中关于等候行列的功用,确保获取锁的次序的公正性
2. 根本完成
在 ReentrantLock 类的 JDK1.8 版别中,关于 ReentrantLock 的根本完成如下:
public class ReentrantLock implements Lock, java.io.Serializable {
private static final long serialVersionUID = 7373984872572414699 L;
/**
* ReentrantLock锁-界说支撑同步器完成
*/
private final Sync sync;
/**
* ReentrantLock锁-根据AQS界说支撑同步器完成
*/
abstract static class Sync extends AbstractQueuedSynchronizer {
private static final long serialVersionUID = -5179523762034025860 L;
/**
* ReentrantLock锁-界说支撑同步器Sync获取锁办法
*/
abstract void lock();
//......其他办法代码
}
/**
* ReentrantLock锁-结构同步器默许作业形式(默许非公正形式)
*/
public ReentrantLock() {
sync = new NonfairSync();
}
/**
* ReentrantLock锁-结构同步器指定作业形式(可选公正/非公正形式)
*/
public ReentrantLock(boolean fair) {
sync = fair ? new FairSync() : new NonfairSync();
}
/**
* ReentrantLock锁-获取锁(一般形式)
*/
public void lock() {
sync.lock();
}
/**
* ReentrantLock锁-开释锁
*/
public void unlock() {
sync.release(1);
}
/**
* ReentrantLock锁-创立锁的条件机制
*/
public Condition newCondition() {
return sync.newCondition();
}
//......其他办法代码
}
- 内部同步器:根据 AQS 根底同步器封装和界说了一个静态内部 Sync 笼统类,其间笼统了一个内置锁 lock () 办法
- 同步器作业形式:供给了 2 个结构办法,其间无参数结构办法表明的是默许的作业形式,有参数结构办法首要根据参数来完成指定的作业形式
- 获取锁: 首要是供给了 lock () 办法,调用的静态内部 Sync 笼统类内置锁 lock () 办法,而本质上是 AQS 同步器中的 acquire () 办法
- 开释锁: 首要是供给了 unlock () 办法,而本质上是调用的 AQS 同步器中的 release () 办法
- 创立条件行列: 首要是根据 Condition 接口界说了 newCondition () 办法,调用的静态内部 Sync 笼统类 ewCondition () 办法,而本质上是调用的 AQS 同步器中的 ConditionObject 中的 newCondition () 办法
2.1 根据 AQS 同步器封装静态内部 Sync 笼统类
abstract static class Sync extends AbstractQueuedSynchronizer {
private static final long serialVersionUID = -5179523762034025860L;
/**
* ReentrantLock锁-内部同步器Sync的内置加锁办法
*/
abstract void lock();
/**
* ReentrantLock锁-内部同步器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;
}
/**
* ReentrantLock锁-内部同步器Sync的测验开释
*/
protected final boolean tryRelease(int releases) {
int c = getState() - releases;
if (Thread.currentThread() != getExclusiveOwnerThread())
throw new IllegalMonitorStateException();
boolean free = false;
if (c == 0) {
free = true;
setExclusiveOwnerThread(null);
}
setState(c);
return free;
}
/**
* ReentrantLock锁-内部同步器Sync的查看线程是否独占
*/
protected final boolean isHeldExclusively() {
// While we must in general read state before owner,
// we don't need to do so to check if current thread is owner
return getExclusiveOwnerThread() == Thread.currentThread();
}
/**
* ReentrantLock锁-内部同步器Sync的条件机制
*/
final ConditionObject newCondition() {
return new ConditionObject();
}
/**
* ReentrantLock锁-内部同步器Sync的判断锁持有者
*/
final Thread getOwner() {
return getState() == 0 ? null : getExclusiveOwnerThread();
}
/**
* ReentrantLock锁-内部同步器Sync的独占状况
*/
final int getHoldCount() {
return isHeldExclusively() ? getState() : 0;
}
/**
* ReentrantLock锁-内部同步器Sync的是否被锁
*/
final boolean isLocked() {
return getState() != 0;
}
/**
* ReentrantLock锁-内部同步器Sync的流化处理目标
*/
private void readObject(java.io.ObjectInputStream s)
throws java.io.IOException, ClassNotFoundException {
s.defaultReadObject();
setState(0); // reset to unlocked state
}
}
- Sync 类:FairSync 公正同步器和 NonfairSync 非公正同步器的笼统父类。
- 校验 isHeldExclusively () 办法: ReentrantLock 锁是归于独占形式,需求当时锁持有线程与当时线程是否共同
- nonfairTryAcquire () 办法: 一般首要用于非公正形式获取锁,其间心是 compareAndSetState 办法和 setExclusiveOwnerThread 办法
- tryRelease () 办法: 其公正 / 非公正形式都是经过 ryRelease () 来开释锁操作
- newCondition () 办法: 根据 AQS 同步器的 ConditionObject 目标封装完成,供给给 ReentrantLock 类运用
- 私有 readObject () 办法:关于输入的目标进行流化处理
特别需求留意的是,我们需求重点关注 nonfairTryAcquire () 办法和 tryRelease () 办法,其间:
- 获取非公正锁 nonfairTryAcquire () 办法:首要是用于获取 AQS 的状况变量 status,其默许取值范围是 0 和 1,其间,0 表明未被加锁,1 表明现已被加锁
- 假如状况变量 status=0,运用 compareAndSetState 办法进行 CAS 原子修改操作,把状况变量修改为 1,而且经过 setExclusiveOwnerThread 设置当时线程为锁的持有线程
- 假如状况变量 status=1,表明当时线程为锁的持有线程,正在进入锁重入操作,状况变量累加 1,超越重入次数时,会抛出 throw new Error (“Maximum lock count exceeded”)
- 开释锁 tryRelease () 办法:首要是查看当时线程是否为锁持有线程,随后 AQS 同步器状况变量减 1,假如不是 throw new IllegalMonitorStateException ()
- 假如状况变量 status=0,表明锁现已开释成功,经过 setExclusiveOwnerThread 设置锁的持有线程为 null,也便是置空锁的持有线程
- 假如状况变量 status !=0, 需求状况变量递减 1 即可,直到锁现已开释成功
2.2 根据 Sync 笼统类封装 FairSync 公正同步器
static final class FairSync extends Sync {
private static final long serialVersionUID = -3000897897090466540L;
/**
* ReentrantLock锁-公正形式-获取锁
*/
final void lock() {
acquire(1);
}
/**
* ReentrantLock锁-公正形式-测验获取锁
*/
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;
}
}
- 完成办法: 首要根据 AQS 封装的内部静态笼统 Sync 同步类完成,运用的 AQS 的独占形式。
- 首要办法: 首要供给了 lock () 和 tryAcquire () 办法,其严厉意义上来说,仅仅只是完成了 tryAcquire () 办法,可是最关键的运用 hasQueuedPredecessors 来确保了锁的公正性。
- 锁获取办法: 首要是选用完全经过行列来完成完成公正机制,即便是查看是否存在等候行列,假如行列之中现已存在其他线程,直接抛弃操作。
2.3 根据 Sync 笼统类封装 NonfairSync 非公正同步器
static final class NonfairSync extends Sync {
private static final long serialVersionUID = 7316153563782823691L;
/**
* ReentrantLock锁-非公正形式-获取锁
*/
final void lock() {
if (compareAndSetState(0, 1))
setExclusiveOwnerThread(Thread.currentThread());
else
acquire(1);
}
/**
* ReentrantLock锁-非公正形式-测验获取锁
*/
protected final boolean tryAcquire(int acquires) {
return nonfairTryAcquire(acquires);
}
}
- 完成办法: 首要根据 AQS 封装的内部静态笼统 Sync 同步类完成,运用的 AQS 的独占形式。
- 首要办法: 首要供给了 lock () 和 tryAcquire () 办法,其严厉意义上来说,仅仅只是完成了 tryAcquire () 办法,直接调用了 Sync 同步类的 nonfairTryAcquire () 办法。
- 锁获取办法: 首要是选用闯入战略来打破锁的公正,也便是一般准备获取锁的线程会先测验获取锁,失利之后才进入行列中。
3. 具体完成
在 ReentrantLock 类的 JDK1.8 版别中,关于 ReentrantLock 的具体完成如下:
public class ReentrantLock implements Lock, java.io.Serializable {
private static final long serialVersionUID = 7373984872572414699 L;
/** Synchronizer providing all implementation mechanics */
private final Sync sync;
/**
* ReentrantLock锁-根据AQS界说支撑同步器完成
*/
abstract static class Sync extends AbstractQueuedSynchronizer {
private static final long serialVersionUID = -5179523762034025860 L;
/**
* ReentrantLock锁-界说支撑同步器Sync获取锁办法
*/
abstract void lock();
//......其他办法代码
}
/**
* ReentrantLock锁-结构同步器默许作业形式(默许非公正形式)
*/
public ReentrantLock() {
sync = new NonfairSync();
}
/**
* ReentrantLock锁-结构同步器指定作业形式(可选公正/非公正形式)
*/
public ReentrantLock(boolean fair) {
sync = fair ? new FairSync() : new NonfairSync();
}
/**
* ReentrantLock锁-获取锁(一般形式)
*/
public void lock() {
sync.lock();
}
/**
* ReentrantLock锁-开释锁
*/
public void unlock() {
sync.release(1);
}
/**
* ReentrantLock锁-创立锁的条件机制
*/
public Condition newCondition() {
return sync.newCondition();
}
/**
* ReentrantLock锁-获取锁(支撑可中止机制)
*/
public void lockInterruptibly() throws InterruptedException {
sync.acquireInterruptibly(1);
}
/**
* ReentrantLock锁-测验获取锁(一般形式)
*/
public boolean tryLock() {
return sync.nonfairTryAcquire(1);
}
/**
* ReentrantLock锁-测验获取锁(支撑超时)
*/
public boolean tryLock(long timeout, TimeUnit unit)
throws InterruptedException {
return sync.tryAcquireNanos(1, unit.toNanos(timeout));
}
/**
* ReentrantLock锁-计算当时线程所持有数量
*/
public int getHoldCount() {
return sync.getHoldCount();
}
/**
* ReentrantLock锁-检测当时线程是否独占
*/
public boolean isHeldByCurrentThread() {
return sync.isHeldExclusively();
}
/**
* ReentrantLock锁-检测是否被加锁
*/
public boolean isLocked() {
return sync.isLocked();
}
/**
* ReentrantLock锁-检测是否公正形式
*/
public final boolean isFair() {
return sync instanceof FairSync;
}
/**
* ReentrantLock锁-获取当时锁持有线程
*/
protected Thread getOwner() {
return sync.getOwner();
}
/**
* ReentrantLock锁-检测轮询线程是否存在行列中
*/
public final boolean hasQueuedThreads() {
return sync.hasQueuedThreads();
}
/**
* ReentrantLock锁-检测线程是否存在行列中
*/
public final boolean hasQueuedThread(Thread thread) {
return sync.isQueued(thread);
}
/**
* ReentrantLock锁-获取行列数量
*/
public final int getQueueLength() {
return sync.getQueueLength();
}
/**
* ReentrantLock锁-获取行列中的所有线程
*/
protected Collection < Thread > getQueuedThreads() {
return sync.getQueuedThreads();
}
/**
* ReentrantLock锁-检测存在条件行列是否入队状况
*/
public boolean hasWaiters(Condition condition) {
if (condition == null)
throw new NullPointerException();
if (!(condition instanceof AbstractQueuedSynchronizer.ConditionObject))
throw new IllegalArgumentException("not owner");
return sync.hasWaiters((AbstractQueuedSynchronizer.ConditionObject) condition);
}
/**
* ReentrantLock锁-获取等候行列的长度
*/
public int getWaitQueueLength(Condition condition) {
if (condition == null)
throw new NullPointerException();
if (!(condition instanceof AbstractQueuedSynchronizer.ConditionObject))
throw new IllegalArgumentException("not owner");
return sync.getWaitQueueLength((AbstractQueuedSynchronizer.ConditionObject) condition);
}
/**
* ReentrantLock锁-获取等候行列的线程目标
*/
protected Collection < Thread > getWaitingThreads(Condition condition) {
if (condition == null)
throw new NullPointerException();
if (!(condition instanceof AbstractQueuedSynchronizer.ConditionObject))
throw new IllegalArgumentException("not owner");
return sync.getWaitingThreads((AbstractQueuedSynchronizer.ConditionObject) condition);
}
}
- 获取锁的办法:首要供给了一般形式 lock () 办法,支撑可中止机制 lockInterruptibly () 办法,无参数 tryLock () 办法以及有参数的支撑超时机制的 tryLock (long timeout, TimeUnit unit) 办法
- 开释锁的办法:首要是 unlock () 办法,直接调用是内部同步器中的 release () 办法
- 条件行列操作:首要供给了获取行列中的线程目标 getQueuedThreads (),检测行列入队 hasWaiters (Condition condition) 办法,以及
- 等候行列操作:首要供给了获取行列中的线程目标 getWaitingThreads (Condition condition),检测行列入队 hasQueuedThread (Thread thread) 办法,以及获取行列长度 getQueueLength () 办法和 getWaitingThreads (Condition condition) 办法
- 其他检测判断:首要有判断是否公正形式 isFair () 办法,是否当时线程独占 isHeldByCurrentThread () 办法,以及是否加锁 isLocked () 等
需求留意的是,在 JDK1.8 版别之后,关于 ReentrantLock 的完成有些纤细的改变,感兴趣的可自行参阅相关版别的源码进行比照剖析。
综上所述,从一定意义上讲,ReentrantLock 是一种可重入的独占 (互斥) 锁,归于 AQS 根底笼统行列同步器中独占形式孵化的产物,支撑公正形式与非公正形式,默许选用非公正形式。