ava并发编程解析 | 基于JDK源码解析Java领域中并发锁之StampedLock锁的设计思想与实现原理 (三)
苍穹之边,浩瀚之挚,眰恦之美; 悟心悟性,善始善终,惟善惟道! —— 朝槿《朝槿兮年说》
写在开头
在并发编程领域,有两大核心问题:一个是互斥,即同一时刻只允许一个线程访问共享资源;另一个是同步,即线程之间如何通信、协作。
主要原因是,对于多线程实现实现并发,一直以来,多线程都存在2个问题:
- 线程之间内存共享,需要通过加锁进行控制,但是加锁会导致性能下降,同时复杂的加锁机制也会增加编程编码难度
- 过多线程造成线程之间的上下文切换,导致效率低下
因此,在并发编程领域中,一直有一个很重要的设计原则: “ 不要通过内存共享来实现通信,而应该通过通信来实现内存共享。”
简单来说,就是尽可能通过消息通信,而不是内存共享来实现进程或者线程之间的同步。
关健术语
本文用到的一些关键词语以及常用术语,主要如下:
- 并发(Concurrent): 在操作系统中,是指一个时间段中有几个程序都处于已启动运行到运行完毕之间,且这几个程序都是在同一个处理机上运行。
- 并行(Parallel): 当系统有一个以上CPU时,当一个CPU执行一个进程时,另一个CPU可以执行另一个进程,两个进程互不抢占CPU资源,可以同时进行。
- 信号量(Semaphore): 是在多线程环境下使用的一种设施,是可以用来保证两个或多个关键代码段不被并发调用,也是作系统用来解决并发中的互斥和同步问题的一种方法。
- 信号量机制(Semaphores): 用来解决同步/互斥的问题的,它是1965年,荷兰学者 Dijkstra提出了一种卓有成效的实现进程互斥与同步的方法。
- 管程(Monitor) : 一般是指管理共享变量以及对共享变量的操作过程,让它们支持并发的一种机制。
- 互斥(Mutual Exclusion):一个公共资源同一时刻只能被一个进程或线程使用,多个进程或线程不能同时使用公共资源。即就是同一时刻只允许一个线程访问共享资源的问题。
- 同步(Synchronization):两个或两个以上的进程或线程在运行过程中协同步调,按预定的先后次序运行。即就是线程之间如何通信、协作的问题。
- 对象池(Object Pool): 指的是一次性创建出 N 个对象,之后所有的线程重复利用这 N 个对象,当然对象在被释放前,也是不允许其他线程使用的, 一般指保存实例对象的容器。
基本概述
在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领域中各种锁实现的基本理论时和实现思想。
五.StampedLock(印戳锁)的设计与实现
在Java领域中,StampedLock(印戳锁)是针对于Java多线程并发控制中引入一个共享锁定义读操作与独占锁定义读操作等场景共同组合构成一把锁来提高并发,主要是基于自定义API操作实现的一种并发控制工具类。
1. 设计思想
StampedLock(印戳锁)是对ReentrantReadWriteLock读写锁的一 种改进,主要的改进为:在没有写只有读的场景下,StampedLock支持 不用加读锁而是直接进行读操作,最大程度提升读的效率,只有在发 生过写操作之后,再加读锁才能进行读操作。
一般来说,StampedLock 里的写锁和悲观读锁加锁成功之后,都会返回一个 stamp;然后解锁的时候,需要传入这个 stamp。
1.1 印戳锁的基本理论
虽然基于AQS基础同步器实现了各种锁,但是由于采用的自旋锁+CAS操作方式会导致如下两个问题:
- CAS恶性空自旋会浪费大量的CPU资源
- 在SMP架构的CPU上会导致“总线风暴”问题
解决CAS恶性空自旋的有效方式之一是以空间换时间,较为常见的 方案有两种:分散操作热点和使用队列削峰。
基于这个基础,在JDK1.8版本中,基于使用队列削峰的方式,自定义API操作,提供了StampedLock(印戳锁)的实现。
简单来说,StampedLock(印戳锁)提供了三种锁的实现模式,其中:
- 悲观读锁:与ReadWriteLock的读锁类似,多个线程可以同 时获取悲观读锁,悲观读锁是一个共享锁。
- 乐观读锁:相当于直接操作数据,不加任何锁,连读锁都不 要。
- 写锁:与ReadWriteLock的写锁类似,写锁和悲观读锁是互 斥的。虽然写锁与乐观读锁不会互斥,但是在数据被更新之后,之前 通过乐观读锁获得的数据已经变成了脏数据。
1.1 印戳锁的实现思想
StampedLock(印戳锁)与其他显式锁不同的是,主要是是最早在JDK1.8版本中提供的,从设计思想上来看,主要包括共享状态变量机制,内置的等待数据队列,读锁视图,写锁视图以及读写锁视图等5个核心要素。其中:
- 共享状态变量机制:主要是在内部封装一些静态私有的常量,用于描述各个模式之间的状态描述等。
- 内置的等待数据队列:主要是自定义实现一个基于CLH锁的等待队列
- 读锁视图:基于Lock接口实现一个对应读锁的视图
- 写锁视图:基于Lock接口实现一个对应写锁的视图
- 读写锁视图:基于ReadWriteLock接口实现一个包含读锁和写锁的视图
2. 基本实现
在StampedLock(印戳锁)类的JDK1.8版本中,对于StampedLock的基本实现如下:
/** StampedLock锁-最早在JDK1.8中实现的 */
public class StampedLock implements java.io.Serializable {
private static final long serialVersionUID = -6001602636862214147L;
/** StampedLock锁-自旋控制的最大允许核心线程数 */
private static final int NCPU = Runtime.getRuntime().availableProcessors();
/** StampedLock锁-等待队列自旋控制的最大自旋阈值 */
private static final int SPINS = (NCPU > 1) ? 1 << 6 : 0;
/** StampedLock锁-等待队列头节点自旋控制的最大自旋阈值 */
private static final int HEAD_SPINS = (NCPU > 1) ? 1 << 10 : 0;
/** StampedLock锁-等待队列头节点自旋控制的最大自旋阈值 */
private static final int MAX_HEAD_SPINS = (NCPU > 1) ? 1 << 16 : 0;
/** StampedLock锁-进入阻塞之前的最大重试次数 */
private static final int OVERFLOW_YIELD_RATE = 7; // must be power 2 - 1
//... 其他锁资源的状态常量
/** StampedLock锁-CLH队列的头部(head)节点 */
private transient volatile WNode whead;
/** StampedLock锁-CLH队列的尾部(tail)节点 */
private transient volatile WNode wtail;
/** StampedLock锁-读锁视图*/
transient ReadLockView readLockView;
/** StampedLock锁-写锁视图*/
transient WriteLockView writeLockView;
/** StampedLock锁-读写锁视图 */
transient ReadWriteLockView readWriteLockView;
/** StampedLock锁-锁的最原始的状态初始值 */
private static final long ORIGIN = WBIT << 1;
/** StampedLock锁-各种锁的同步状态变量 */
private transient volatile long state;
/** StampedLock锁-读锁的溢出的拓展标记 */
private transient int readerOverflow;
/** StampedLock锁-构造方法 */
public StampedLock() {
state = ORIGIN;
}
/** StampedLock锁-实例化ReadLock方法 */
public Lock asReadLock() {
ReadLockView v;
return ((v = readLockView) != null ? v :
(readLockView = new ReadLockView()));
}
/** StampedLock锁-实例化WriteLock方法 */
public Lock asWriteLock() {
WriteLockView v;
return ((v = writeLockView) != null ? v :
(writeLockView = new WriteLockView()));
}
/** StampedLock锁-实例化ReadWriteLock方法 */
public ReadWriteLock asReadWriteLock() {
ReadWriteLockView v;
return ((v = readWriteLockView) != null ? v :
(readWriteLockView = new ReadWriteLockView()));
}
/** StampedLock锁-获取ReadLock方法 */
public long readLock() {
long s = state, next; // bypass acquireRead on common uncontended case
return ((whead == wtail && (s & ABITS) < RFULL &&
U.compareAndSwapLong(this, STATE, s, next = s + RUNIT)) ?
next : acquireRead(false, 0L));
}
/** StampedLock锁-获取WriteLock方法 */
public long writeLock() {
long s, next; // bypass acquireWrite in fully unlocked case only
return ((((s = state) & ABITS) == 0L &&
U.compareAndSwapLong(this, STATE, s, next = s + WBIT)) ?
next : acquireWrite(false, 0L));
}
//... 其他代码
}
2.1 共享状态变量机制
/** StampedLock锁-最早在JDK1.8中实现的 */
public class StampedLock implements java.io.Serializable {
/** StampedLock锁-自旋控制的最大允许核心线程数 */
private static final int NCPU = Runtime.getRuntime().availableProcessors();
/** StampedLock锁-等待队列自旋控制的最大自旋阈值 */
private static final int SPINS = (NCPU > 1) ? 1 << 6 : 0;
/** StampedLock锁-等待队列头节点自旋控制的最大自旋阈值 */
private static final int HEAD_SPINS = (NCPU > 1) ? 1 << 10 : 0;
/** StampedLock锁-等待队列头节点自旋控制的最大自旋阈值 */
private static final int MAX_HEAD_SPINS = (NCPU > 1) ? 1 << 16 : 0;
/** StampedLock锁-进入阻塞之前的最大重试次数 */
private static final int OVERFLOW_YIELD_RATE = 7; // must be power 2 - 1
// Values for lock state and stamp operations
/** StampedLock锁-读锁移动的位数 */
private static final long RUNIT = 1L;
/** StampedLock锁-写锁移动的位数 */
private static final long WBIT = 1L << LG_READERS;
/** StampedLock锁-读锁移动的位数 */
private static final long RBITS = WBIT - 1L;
/** StampedLock锁-读锁移动的位数 */
private static final long RFULL = RBITS - 1L;
/** StampedLock锁-读写锁移动的位数 */
private static final long ABITS = RBITS | WBIT;
/** StampedLock锁-读写锁移动的位数 */
private static final long SBITS = ~RBITS;
// Special value from cancelled acquire methods so caller can throw IE
/** StampedLock锁-线程对象中断标识 */
private static final long INTERRUPTED = 1L;
// Values for node status; order matters
/** StampedLock锁-最早在JDK1.8中实现的 */
private static final int WAITING = -1;
/** StampedLock锁-最早在JDK1.8中实现的 */
private static final int CANCELLED = 1;
// Modes for nodes (int not boolean to allow arithmetic)
/** StampedLock锁-用于表示在队列之中是读模式 */
private static final int RMODE = 0;
/** StampedLock锁-用于表示在队列之中是写模式 */
private static final int WMODE = 1;
//... 其他代码
// Unsafe mechanics
/** StampedLock锁-实例化Unsafe对象 */
private static final sun.misc.Unsafe U;
/** StampedLock锁-状态 */
private static final long STATE;
/** StampedLock锁-头部节点 */
private static final long WHEAD;
/** StampedLock锁-尾部节点 */
private static final long WTAIL;
/** StampedLock锁-后继节点 */
private static final long WNEXT;
/** StampedLock锁-节点状态 */
private static final long WSTATUS;
/** StampedLock锁-节点链表 */
private static final long WCOWAIT;
/** StampedLock锁-中断标识 */
private static final long PARKBLOCKER;
static {
try {
U = sun.misc.Unsafe.getUnsafe();
Class<?> k = StampedLock.class;
Class<?> wk = WNode.class;
STATE = U.objectFieldOffset
(k.getDeclaredField("state"));
WHEAD = U.objectFieldOffset
(k.getDeclaredField("whead"));
WTAIL = U.objectFieldOffset
(k.getDeclaredField("wtail"));
WSTATUS = U.objectFieldOffset
(wk.getDeclaredField("status"));
WNEXT = U.objectFieldOffset
(wk.getDeclaredField("next"));
WCOWAIT = U.objectFieldOffset
(wk.getDeclaredField("cowait"));
Class<?> tk = Thread.class;
PARKBLOCKER = U.objectFieldOffset
(tk.getDeclaredField("parkBlocker"));
} catch (Exception e) {
throw new Error(e);
}
}
}
对于StampedLock锁中对于各种资源的标记,其封装了一系列的常量,主要可以分为以下几个方面,其中:
- 核心资源常量标识:是对线程操作资源的提供的常量封装,其中:
- NCPU:自旋控制的核心线程数量,主要通过Runtime.getRuntime().availableProcessors()获取设置。
- SPINS:等待队列自旋控制的最大自旋阈值,主要通过 (_NCPU _> 1) ? 1 << 6 : 0获取设置
- HEAD_SPINS: 等待队列头节点自旋控制的自旋阈值,主要通过 (_NCPU _> 1) ? 1 << 10 : 0获取设置
- MAX_HEAD_SPINS:等待队列头节点自旋控制的最大自旋阈值,主要通过(_NCPU _> 1) ? 1 << 16 : 0获取设置
- OVERFLOW_YIELD_RATE:线程让步操作等待的自旋阈值,默认值为7
- LG_READERS:读锁溢出的最大阈值,默认值为7
- _INTERRUPTED:线程中断标识,_默认值为1L
- 锁状态值设置标识:
- ORIGIN:锁状态的初始值,默认值为WBIT << 1,如果分配失败默认设置为0
- 锁状态的操作标识:
- RUNIT:读锁移动的位数,默认值为 1
- WBIT:写锁移动的位数,默认值为1L << LG_READERS
- RBITS:读锁移动的位数_,_默认值为_WBIT _- 1L
- RFULL:移动的位数,默认值为_RBITS _- 1L
- ABITS:锁移动的位数,默认值为 _RBITS _| WBIT
- SBITS:锁移动的位数,默认值为 ~RBITS
- 等待队列节点标识:
- WAITING:等待状态的初始值,默认值为-1
- CANCELLED:取消状态的初始值,默认值为1
- 读写锁的模式标识:
- RMODE:读锁模式,默认值为0
- WMODE:写锁模式,默认值为1
- CAS操作状态标识:封装了CAS操作状态标识,还通过反射实例化了Unsafe对象实例。
2.2 内置的等待队列WNode
/** StampedLock锁-最早在JDK1.8中实现的 */
public class StampedLock implements java.io.Serializable {
/** Wait nodes */
static final class WNode {
/** StampedLock锁-队列前驱节点 */
volatile WNode prev;
/** StampedLock锁-队列后驱节点 */
volatile WNode next;
/** StampedLock锁-锁的存储列表 */
volatile WNode cowait; // list of linked readers
/** StampedLock锁-线程对象 */
volatile Thread thread; // non-null while possibly parked
/** StampedLock锁-锁的状态 */
volatile int status; // 0, WAITING, or CANCELLED
/** StampedLock锁-锁的模式 */
final int mode; // RMODE or WMODE
WNode(int m, WNode p) { mode = m; prev = p; }
}
/** Head of CLH queue */
/** StampedLock锁-头部节点 */
private transient volatile WNode whead;
/** Tail (last) of CLH queue */
/** StampedLock锁-尾部节点 */
private transient volatile WNode wtail;
}
对于StampedLock锁对于等待队列的实现,主要包含以下几个方面的内容,其中:
- 封装了一个等待队列WNode的静态内部类,其中:
- prev:等待队列的前驱节点
- next:等待队列的后驱节点
- cowait:表示依据锁标记存储当前线程入队的情况,队列锁列表
- thread: 线程对象,一般都是当前获取锁的线程
- status:用于表示锁的状态变量,对应着常量0,WAITING(-1), CANCELLED(1),其中,0表示正常状态,WAITING(-1)为等待状态,CANCELLED(1)为取消状态。
- mode:用于表示锁的模式,对应着常量RMODE和WMODE,其中RMODE为写模式,WMOD为读模式
- 构造方法WNode(int m, WNode p):用于实例化WNode对象,实现一个等待队列
- 实例化等待队列对象,主要封装一个头部节点whead和尾部节点wtail的对象
2.3 共用的读锁核心处理逻辑
首先,对于StampedLock锁的读锁视图与写锁视图的队列操作,有一个核心的处理逻辑:
/** StampedLock锁-最早在JDK1.8中实现的 */
public class StampedLock implements java.io.Serializable {
/** StampedLock锁-取消入队列 */
private long cancelWaiter(WNode node, WNode group, boolean interrupted) {
if (node != null && group != null) {
Thread w;
node.status = CANCELLED;
// unsplice cancelled nodes from group
for (WNode p = group, q; (q = p.cowait) != null;) {
if (q.status == CANCELLED) {
U.compareAndSwapObject(p, WCOWAIT, q, q.cowait);
p = group; // restart
}
else
p = q;
}
if (group == node) {
for (WNode r = group.cowait; r != null; r = r.cowait) {
if ((w = r.thread) != null)
U.unpark(w); // wake up uncancelled co-waiters
}
for (WNode pred = node.prev; pred != null; ) { // unsplice
WNode succ, pp; // find valid successor
while ((succ = node.next) == null ||
succ.status == CANCELLED) {
WNode q = null; // find successor the slow way
for (WNode t = wtail; t != null && t != node; t = t.prev)
if (t.status != CANCELLED)
q = t; // don't link if succ cancelled
if (succ == q || // ensure accurate successor
U.compareAndSwapObject(node, WNEXT,
succ, succ = q)) {
if (succ == null && node == wtail)
U.compareAndSwapObject(this, WTAIL, node, pred);
break;
}
}
if (pred.next == node) // unsplice pred link
U.compareAndSwapObject(pred, WNEXT, node, succ);
if (succ != null && (w = succ.thread) != null) {
succ.thread = null;
U.unpark(w); // wake up succ to observe new pred
}
if (pred.status != CANCELLED || (pp = pred.prev) == null)
break;
node.prev = pp; // repeat if new pred wrong/cancelled
U.compareAndSwapObject(pp, WNEXT, pred, succ);
pred = pp;
}
}
}
WNode h; // Possibly release first waiter
while ((h = whead) != null) {
long s; WNode q; // similar to release() but check eligibility
if ((q = h.next) == null || q.status == CANCELLED) {
for (WNode t = wtail; t != null && t != h; t = t.prev)
if (t.status <= 0)
q = t;
}
if (h == whead) {
if (q != null && h.status == 0 &&
((s = state) & ABITS) != WBIT && // waiter is eligible
(s == 0L || q.mode == RMODE))
release(h);
break;
}
}
return (interrupted || Thread.interrupted()) ? INTERRUPTED : 0L;
}
}
其次,对于StampedLock锁的读锁视图的实现作来看,主要核心处理如下:
/** StampedLock锁-最早在JDK1.8中实现的 */
public class StampedLock implements java.io.Serializable {
/** StampedLock锁-获取读锁 */
private long acquireRead(boolean interruptible, long deadline) {
WNode node = null, p;
for (int spins = -1;;) {
WNode h;
if ((h = whead) == (p = wtail)) {
for (long m, s, ns;;) {
if ((m = (s = state) & ABITS) < RFULL ?
U.compareAndSwapLong(this, STATE, s, ns = s + RUNIT) :
(m < WBIT && (ns = tryIncReaderOverflow(s)) != 0L))
return ns;
else if (m >= WBIT) {
if (spins > 0) {
if (LockSupport.nextSecondarySeed() >= 0)
--spins;
}
else {
if (spins == 0) {
WNode nh = whead, np = wtail;
if ((nh == h && np == p) || (h = nh) != (p = np))
break;
}
spins = SPINS;
}
}
}
}
if (p == null) { // initialize queue
WNode hd = new WNode(WMODE, null);
if (U.compareAndSwapObject(this, WHEAD, null, hd))
wtail = hd;
}
else if (node == null)
node = new WNode(RMODE, p);
else if (h == p || p.mode != RMODE) {
if (node.prev != p)
node.prev = p;
else if (U.compareAndSwapObject(this, WTAIL, p, node)) {
p.next = node;
break;
}
}
else if (!U.compareAndSwapObject(p, WCOWAIT,
node.cowait = p.cowait, node))
node.cowait = null;
else {
for (;;) {
WNode pp, c; Thread w;
if ((h = whead) != null && (c = h.cowait) != null &&
U.compareAndSwapObject(h, WCOWAIT, c, c.cowait) &&
(w = c.thread) != null) // help release
U.unpark(w);
if (h == (pp = p.prev) || h == p || pp == null) {
long m, s, ns;
do {
if ((m = (s = state) & ABITS) < RFULL ?
U.compareAndSwapLong(this, STATE, s,
ns = s + RUNIT) :
(m < WBIT &&
(ns = tryIncReaderOverflow(s)) != 0L))
return ns;
} while (m < WBIT);
}
if (whead == h && p.prev == pp) {
long time;
if (pp == null || h == p || p.status > 0) {
node = null; // throw away
break;
}
if (deadline == 0L)
time = 0L;
else if ((time = deadline - System.nanoTime()) <= 0L)
return cancelWaiter(node, p, false);
Thread wt = Thread.currentThread();
U.putObject(wt, PARKBLOCKER, this);
node.thread = wt;
if ((h != pp || (state & ABITS) == WBIT) &&
whead == h && p.prev == pp)
U.park(false, time);
node.thread = null;
U.putObject(wt, PARKBLOCKER, null);
if (interruptible && Thread.interrupted())
return cancelWaiter(node, p, true);
}
}
}
}
for (int spins = -1;;) {
WNode h, np, pp; int ps;
if ((h = whead) == p) {
if (spins < 0)
spins = HEAD_SPINS;
else if (spins < MAX_HEAD_SPINS)
spins <<= 1;
for (int k = spins;;) { // spin at head
long m, s, ns;
if ((m = (s = state) & ABITS) < RFULL ?
U.compareAndSwapLong(this, STATE, s, ns = s + RUNIT) :
(m < WBIT && (ns = tryIncReaderOverflow(s)) != 0L)) {
WNode c; Thread w;
whead = node;
node.prev = null;
while ((c = node.cowait) != null) {
if (U.compareAndSwapObject(node, WCOWAIT,
c, c.cowait) &&
(w = c.thread) != null)
U.unpark(w);
}
return ns;
}
else if (m >= WBIT &&
LockSupport.nextSecondarySeed() >= 0 && --k <= 0)
break;
}
}
else if (h != null) {
WNode c; Thread w;
while ((c = h.cowait) != null) {
if (U.compareAndSwapObject(h, WCOWAIT, c, c.cowait) &&
(w = c.thread) != null)
U.unpark(w);
}
}
if (whead == h) {
if ((np = node.prev) != p) {
if (np != null)
(p = np).next = node; // stale
}
else if ((ps = p.status) == 0)
U.compareAndSwapInt(p, WSTATUS, 0, WAITING);
else if (ps == CANCELLED) {
if ((pp = p.prev) != null) {
node.prev = pp;
pp.next = node;
}
}
else {
long time;
if (deadline == 0L)
time = 0L;
else if ((time = deadline - System.nanoTime()) <= 0L)
return cancelWaiter(node, node, false);
Thread wt = Thread.currentThread();
U.putObject(wt, PARKBLOCKER, this);
node.thread = wt;
if (p.status < 0 &&
(p != h || (state & ABITS) == WBIT) &&
whead == h && node.prev == p)
U.park(false, time);
node.thread = null;
U.putObject(wt, PARKBLOCKER, null);
if (interruptible && Thread.interrupted())
return cancelWaiter(node, node, true);
}
}
}
}
}
然后,对于StampedLock锁的写锁视图的实现作来看,主要核心处理如下:
/** StampedLock锁-最早在JDK1.8中实现的 */
public class StampedLock implements java.io.Serializable {
/** StampedLock锁-获取写锁 */
private long acquireWrite(boolean interruptible, long deadline) {
WNode node = null, p;
for (int spins = -1;;) { // spin while enqueuing
long m, s, ns;
if ((m = (s = state) & ABITS) == 0L) {
if (U.compareAndSwapLong(this, STATE, s, ns = s + WBIT))
return ns;
}
else if (spins < 0)
spins = (m == WBIT && wtail == whead) ? SPINS : 0;
else if (spins > 0) {
if (LockSupport.nextSecondarySeed() >= 0)
--spins;
}
else if ((p = wtail) == null) { // initialize queue
WNode hd = new WNode(WMODE, null);
if (U.compareAndSwapObject(this, WHEAD, null, hd))
wtail = hd;
}
else if (node == null)
node = new WNode(WMODE, p);
else if (node.prev != p)
node.prev = p;
else if (U.compareAndSwapObject(this, WTAIL, p, node)) {
p.next = node;
break;
}
}
for (int spins = -1;;) {
WNode h, np, pp; int ps;
if ((h = whead) == p) {
if (spins < 0)
spins = HEAD_SPINS;
else if (spins < MAX_HEAD_SPINS)
spins <<= 1;
for (int k = spins;;) { // spin at head
long s, ns;
if (((s = state) & ABITS) == 0L) {
if (U.compareAndSwapLong(this, STATE, s,
ns = s + WBIT)) {
whead = node;
node.prev = null;
return ns;
}
}
else if (LockSupport.nextSecondarySeed() >= 0 &&
--k <= 0)
break;
}
}
else if (h != null) { // help release stale waiters
WNode c; Thread w;
while ((c = h.cowait) != null) {
if (U.compareAndSwapObject(h, WCOWAIT, c, c.cowait) &&
(w = c.thread) != null)
U.unpark(w);
}
}
if (whead == h) {
if ((np = node.prev) != p) {
if (np != null)
(p = np).next = node; // stale
}
else if ((ps = p.status) == 0)
U.compareAndSwapInt(p, WSTATUS, 0, WAITING);
else if (ps == CANCELLED) {
if ((pp = p.prev) != null) {
node.prev = pp;
pp.next = node;
}
}
else {
long time; // 0 argument to park means no timeout
if (deadline == 0L)
time = 0L;
else if ((time = deadline - System.nanoTime()) <= 0L)
return cancelWaiter(node, node, false);
Thread wt = Thread.currentThread();
U.putObject(wt, PARKBLOCKER, this);
node.thread = wt;
if (p.status < 0 && (p != h || (state & ABITS) != 0L) &&
whead == h && node.prev == p)
U.park(false, time); // emulate LockSupport.park
node.thread = null;
U.putObject(wt, PARKBLOCKER, null);
if (interruptible && Thread.interrupted())
return cancelWaiter(node, node, true);
}
}
}
}
}
最后,综合对于StampedLock锁的读锁和写锁的获取和释放等操作来看,主要核心处理都会调用以下2个方法,其中:
/** StampedLock锁-最早在JDK1.8中实现的 */
public class StampedLock implements java.io.Serializable {
/** StampedLock锁-读锁溢出递增处理方法 */
private long tryIncReaderOverflow(long s) {
// assert (s & ABITS) >= RFULL;
if ((s & ABITS) == RFULL) {
if (U.compareAndSwapLong(this, STATE, s, s | RBITS)) {
++readerOverflow;
state = s;
return s;
}
}
else if ((LockSupport.nextSecondarySeed() &
OVERFLOW_YIELD_RATE) == 0)
Thread.yield();
return 0L;
}
/** StampedLock锁-读锁溢出递减处理方法 */
private long tryDecReaderOverflow(long s) {
// assert (s & ABITS) >= RFULL;
if ((s & ABITS) == RFULL) {
if (U.compareAndSwapLong(this, STATE, s, s | RBITS)) {
int r; long next;
if ((r = readerOverflow) > 0) {
readerOverflow = r - 1;
next = s;
}
else
next = s - RUNIT;
state = next;
return next;
}
}
else if ((LockSupport.nextSecondarySeed() &
OVERFLOW_YIELD_RATE) == 0)
Thread.yield();
return 0L;
}
}
- tryIncReaderOverflow()方法:主要是实现对于锁获取自旋时最大重试次数的递增运算。其中:
- 对于满足_stamp_ >= RFULL条件时,利用compareAndSwapLong()方法来实现CAS操作加持修改状态值。对于readerOverflow作自增运算后返回一个_stamp,可能存在更新和释放操作。_
- 否则,利用LockSupport.nextSecondarySeed() 判断,对于线程做让步处理,默认返回0
- tryDecReaderOverflow()方法:主要是实现对于锁获取自旋时最大重试次数的递减运算。其中:
- 对于满足_stamp_ == RFULL条件时,利用compareAndSwapLong()方法来实现CAS操作加持修改状态值。对于readerOverflow>0做递减运算后返回一个_stamp,可能存在更新和释放操作。_
- 否则,利用LockSupport.nextSecondarySeed() 判断,对于线程做让步处理,默认返回0
2.4 基于Lock接口实现的ReadLockView
/** StampedLock锁-最早在JDK1.8中实现的 */
public class StampedLock implements java.io.Serializable {
/** StampedLock锁-ReadLockView */
final class ReadLockView implements Lock {
/** StampedLock锁-获取锁 */
public void lock() { readLock(); }
/** StampedLock锁-获取可中断锁 */
public void lockInterruptibly() throws InterruptedException {
readLockInterruptibly();
}
/** StampedLock锁-尝试获取锁 */
public boolean tryLock() { return tryReadLock() != 0L; }
/** StampedLock锁-尝试获取可超时锁 */
public boolean tryLock(long time, TimeUnit unit)
throws InterruptedException {
return tryReadLock(time, unit) != 0L;
}
/** StampedLock锁-释放 */
public void unlock() { unstampedUnlockRead(); }
/** StampedLock锁-不支持条件变量定义 */
public Condition newCondition() {
throw new UnsupportedOperationException();
}
}
/** StampedLock锁-实例化ReadLock方法 */
public Lock asReadLock() {
ReadLockView v;
return ((v = readLockView) != null ? v :
(readLockView = new ReadLockView()));
}
/** StampedLock锁-实例化ReadLock方法 */
public long readLock() {
long s = state, next; // bypass acquireRead on common uncontended case
return ((whead == wtail && (s & ABITS) < RFULL &&
U.compareAndSwapLong(this, STATE, s, next = s + RUNIT)) ?
next : acquireRead(false, 0L));
}
/** StampedLock锁-实例化ReadLock方法 */
public long tryReadLock() {
for (;;) {
long s, m, next;
if ((m = (s = state) & ABITS) == WBIT)
return 0L;
else if (m < RFULL) {
if (U.compareAndSwapLong(this, STATE, s, next = s + RUNIT))
return next;
}
else if ((next = tryIncReaderOverflow(s)) != 0L)
return next;
}
}
/** StampedLock锁-实例化ReadLock方法 */
public long tryReadLock(long time, TimeUnit unit)
throws InterruptedException {
long s, m, next, deadline;
long nanos = unit.toNanos(time);
if (!Thread.interrupted()) {
if ((m = (s = state) & ABITS) != WBIT) {
if (m < RFULL) {
if (U.compareAndSwapLong(this, STATE, s, next = s + RUNIT))
return next;
}
else if ((next = tryIncReaderOverflow(s)) != 0L)
return next;
}
if (nanos <= 0L)
return 0L;
if ((deadline = System.nanoTime() + nanos) == 0L)
deadline = 1L;
if ((next = acquireRead(true, deadline)) != INTERRUPTED)
return next;
}
throw new InterruptedException();
}
/** StampedLock锁-释放锁方法 */
final void unstampedUnlockRead() {
// 自旋操作
for (;;) {
long s, m; WNode h;
if ((m = (s = state) & ABITS) == 0L || m >= WBIT)
throw new IllegalMonitorStateException();
else if (m < RFULL) {
if (U.compareAndSwapLong(this, STATE, s, s - RUNIT)) {
if (m == RUNIT && (h = whead) != null && h.status != 0)
release(h);
break;
}
}
else if (tryDecReaderOverflow(s) != 0L)
break;
}
}
}
对于ReadLock的实现,主要包含以下几个方面的内容,其中:
- 基本实现方式:基于Lock接口实现,提供了对应的锁获取和释放操作方法,其中:
- lock()方法:一般模式,主要通过StampedLock类中readLock()方法实现
- lockInterruptibly()方法:可中断模式,主要通过StampedLock类中readLockInterruptibly()方法实现
- 无参数tryLock() 方法:尝试获取锁,主要依据StampedLock类中tryReadLock() != 0L来实现
- 有参数tryLock() 方法:尝试获取锁,主要依据StampedLock类中tryReadLock(long time, TimeUnit unit)!= 0L来实现
- unlock()方法:锁的释放,主要通过StampedLock类中unstampedUnlockRead()方法实现
- newCondition() 方法:不支持条件变量的定义,默认设置抛出UnsupportedOperationException
- 对应处理方法:主要是在StampedLock外层实现的操作方法,其中:
- readLock()方法:读锁的实现,主要核心逻辑在acquireRead()方法
- tryReadLock()方法:尝试获取读锁,核心处理逻辑是根据对应的条件返回对应的锁的_stamp,否则抛出_InterruptedException。
- readLockInterruptibly()方法:读锁的可中断机制实现,核心处理逻辑是判断线程是否中断以及利用acquireRead方法验证,条件成立时,返回锁的_stamp,否则抛出_InterruptedException。
- unstampedUnlockRead()方法:释放锁,核心处理逻辑自旋操作+compareAndSwapLong实现。
2.5 基于Lock接口实现的WriteLockView
/** StampedLock锁-最早在JDK1.8中实现的 */
public class StampedLock implements java.io.Serializable {
final class WriteLockView implements Lock {
public void lock() { writeLock(); }
public void lockInterruptibly() throws InterruptedException {
writeLockInterruptibly();
}
public boolean tryLock() { return tryWriteLock() != 0L; }
public boolean tryLock(long time, TimeUnit unit)
throws InterruptedException {
return tryWriteLock(time, unit) != 0L;
}
public void unlock() { unstampedUnlockWrite(); }
public Condition newCondition() {
throw new UnsupportedOperationException();
}
}
/** StampedLock锁-实例化WriteLock方法 */
public Lock asWriteLock() {
WriteLockView v;
return ((v = writeLockView) != null ? v :
(writeLockView = new WriteLockView()));
}
}
对于WriteLockView的实现,主要包含以下几个方面的内容,其中:
- 基本实现方式:基于Lock接口实现,提供了对应的锁获取和释放操作方法,其中:
- lock()方法:一般模式,主要通过StampedLock类中WriteLock()方法实现
- lockInterruptibly()方法:可中断模式,主要通过StampedLock类中writeLockInterruptibly()方法实现
- 无参数tryLock() 方法:尝试获取锁,主要依据StampedLock类中tryWriteLock() != 0L来实现
- 有参数tryLock() 方法:尝试获取锁,主要依据StampedLock类中tryWriteLock(long time, TimeUnit unit) != 0L来实现
- unlock()方法:锁的释放,主要通过StampedLock类中unstampedUnlockWrite()方法实现
- newCondition() 方法:不支持条件变量的定义,默认设置抛出UnsupportedOperationException
- 核心处理方法:主要是在StampedLock外层实现的操作方法,其中:
- writeLock()方法:写锁的实现,主要核心逻辑在acquireWrite()方法
- tryWriteLock()方法:尝试获取写锁,核心处理逻辑是根据对应的条件返回对应的锁的_stamp,否则抛出_InterruptedException。
- writeLockInterruptibly()方法:写锁的可中断机制实现,核心处理逻辑是判断线程是否中断以及利用acquireWrite方法验证,条件成立时,返回锁的_stamp,否则抛出_InterruptedException。
- unstampedUnlockWrite()方法:释放锁,核心处理逻辑主要是通过调用release(WNode h) 方法实现。
2.6 基于ReadWriteLock接口实现ReadWriteLockView
/** StampedLock锁-最早在JDK1.8中实现的 */
public class StampedLock implements java.io.Serializable {
/** StampedLock锁-实例化ReadWriteLock方法 */
final class ReadWriteLockView implements ReadWriteLock {
/** StampedLock锁-ReadLock方法 */
public Lock readLock() { return asReadLock(); }
/** StampedLock锁-WriteLock方法 */
public Lock writeLock() { return asWriteLock(); }
}
/** StampedLock锁-实例化ReadWriteLock方法 */
public ReadWriteLock asReadWriteLock() {
ReadWriteLockView v;
return ((v = readWriteLockView) != null ? v :
(readWriteLockView = new ReadWriteLockView()));
}
}
对于ReadWriteLockView的实现,主要包含两个部分,其中:
- 基于ReadWriteLock接口实现,主要是实现readLock()和writeLock()方法
- 在asReadWriteLock()方法中,实例化ReadWriteLockView对象
3. 具体实现
对于StampedLock的具体实现,我们可以从如下几个方面拆解开来分析:
- 共享锁ReadLock锁获取操作实现: 需要区分悲观读锁和乐观读锁的获取个有不同,一般有默认获取方式和尝试获取两种方式。
- 独占锁WriteLock写锁获取操作实现: 写锁与悲观读锁互斥,一般有默认获取方式和尝试获取两种方式
- 共享锁ReadLock锁释放操作实现: 一般分为全释放和半释放ReadLock锁操作两种方式
- 独占锁WriteLock锁释放操作实现:一般分为全释放和半释放WriteLock锁操作两种方式
接下来,我们便从具体的代码中来分析以上内容的基本实现,以方便我们正确认识和了解StampedLock锁。
3.1 共享锁ReadLock读锁获取操作实现
/** StampedLock锁-最早在JDK1.8中实现的 */
public class StampedLock implements java.io.Serializable {
/** StampedLock锁-悲观读锁-尝试获取锁(默认模式,不支持超时机制) */
public long tryReadLock() {
// 锁自旋转+compareAndSwapLong来CAS操作加持
for (;;) {
long s, m, next;
// [1].直接返回0
if ((m = (s = state) & ABITS) == WBIT)
return 0L;
// [2].compareAndSwapLong来CAS操作加持
else if (m < RFULL) {
if (U.compareAndSwapLong(this, STATE, s, next = s + RUNIT))
return next;
}
// [3].尝试获取读锁溢出处理
else if ((next = tryIncReaderOverflow(s)) != 0L)
return next;
}
}
/** StampedLock锁-悲观读锁-尝试获取锁(指定模式,支持超时机制) */
public long tryReadLock(long time, TimeUnit unit)
throws InterruptedException {
long s, m, next, deadline;
long nanos = unit.toNanos(time);
if (!Thread.interrupted()) {
if ((m = (s = state) & ABITS) != WBIT) {
if (m < RFULL) {
if (U.compareAndSwapLong(this, STATE, s, next = s + RUNIT))
return next;
}
else if ((next = tryIncReaderOverflow(s)) != 0L)
return next;
}
if (nanos <= 0L)
return 0L;
if ((deadline = System.nanoTime() + nanos) == 0L)
deadline = 1L;
if ((next = acquireRead(true, deadline)) != INTERRUPTED)
return next;
}
throw new InterruptedException();
}
/** StampedLock锁-乐观读锁-尝试获取锁 */
public long tryOptimisticRead() {
long s;
return (((s = state) & WBIT) == 0L) ? (s & SBITS) : 0L;
}
}
对于读锁的获取来说,都属于是共享锁,主要提供了以下几种方式:
- 无参数tryReadLock()方法:悲观读锁的获取方式,默认模式,不支持超时机制
- 有参数tryReadLock()方法:悲观读锁的获取方式,指定参数模式,支持超时机制
- 无参数tryOptimisticRead()方法:乐观读锁的获取方式,没有加锁操作
3.2 独占锁WriteLock写锁获取操作实现
/** StampedLock锁-最早在JDK1.8中实现的 */
public class StampedLock implements java.io.Serializable {
/** StampedLock锁-获取写锁操作(不支持超时机制) */
public long tryWriteLock() {
long s, next;
return ((((s = state) & ABITS) == 0L &&
U.compareAndSwapLong(this, STATE, s, next = s + WBIT)) ?
next : 0L);
}
/** StampedLock锁-获取写锁操作(支持超时机制) */
public long tryWriteLock(long time, TimeUnit unit)
throws InterruptedException {
long nanos = unit.toNanos(time);
if (!Thread.interrupted()) {
long next, deadline;
if ((next = tryWriteLock()) != 0L)
return next;
if (nanos <= 0L)
return 0L;
if ((deadline = System.nanoTime() + nanos) == 0L)
deadline = 1L;
if ((next = acquireWrite(true, deadline)) != INTERRUPTED)
return next;
}
throw new InterruptedException();
}
}
对于写锁的获取来说,都属于是独占锁,主要提供了以下几种方式:
- 无参数tryWriteLock()方法:默认模式,不支持超时机制
- 有参数tryWriteLock()方法:指定模式,依据参数来实现,支持超时机制
3.3 共享锁ReadLock释放操作实现
/** StampedLock锁-最早在JDK1.8中实现的 */
public class StampedLock implements java.io.Serializable {
/** StampedLock锁-释放锁操作 */
public void unlock(long stamp) {
long a = stamp & ABITS, m, s; WNode h;
while (((s = state) & SBITS) == (stamp & SBITS)) {
if ((m = s & ABITS) == 0L)
break;
else if (m == WBIT) {
if (a != m)
break;
state = (s += WBIT) == 0L ? ORIGIN : s;
if ((h = whead) != null && h.status != 0)
release(h);
return;
}
else if (a == 0L || a >= WBIT)
break;
else if (m < RFULL) {
if (U.compareAndSwapLong(this, STATE, s, s - RUNIT)) {
if (m == RUNIT && (h = whead) != null && h.status != 0)
release(h);
return;
}
}
else if (tryDecReaderOverflow(s) != 0L)
return;
}
throw new IllegalMonitorStateException();
}
/** StampedLock锁-释放读锁 */
public void unlockRead(long stamp) {
long s, m; WNode h;
for (;;) {
if (((s = state) & SBITS) != (stamp & SBITS) ||
(stamp & ABITS) == 0L || (m = s & ABITS) == 0L || m == WBIT)
throw new IllegalMonitorStateException();
if (m < RFULL) {
if (U.compareAndSwapLong(this, STATE, s, s - RUNIT)) {
if (m == RUNIT && (h = whead) != null && h.status != 0)
release(h);
break;
}
}
else if (tryDecReaderOverflow(s) != 0L)
break;
}
}
/** StampedLock锁-悲观读锁-转换升级并释放处理 */
public long tryConvertToReadLock(long stamp) {
long a = stamp & ABITS, m, s, next; WNode h;
while (((s = state) & SBITS) == (stamp & SBITS)) {
if ((m = s & ABITS) == 0L) {
if (a != 0L)
break;
else if (m < RFULL) {
if (U.compareAndSwapLong(this, STATE, s, next = s + RUNIT))
return next;
}
else if ((next = tryIncReaderOverflow(s)) != 0L)
return next;
}
else if (m == WBIT) {
if (a != m)
break;
state = next = s + (WBIT + RUNIT);
if ((h = whead) != null && h.status != 0)
release(h);
return next;
}
else if (a != 0L && a < WBIT)
return stamp;
else
break;
}
return 0L;
}
/** StampedLock锁-乐观读锁-转换升级并释放处理 */
public long tryConvertToOptimisticRead(long stamp) {
long a = stamp & ABITS, m, s, next; WNode h;
U.loadFence();
for (;;) {
if (((s = state) & SBITS) != (stamp & SBITS))
break;
if ((m = s & ABITS) == 0L) {
if (a != 0L)
break;
return s;
}
else if (m == WBIT) {
if (a != m)
break;
state = next = (s += WBIT) == 0L ? ORIGIN : s;
if ((h = whead) != null && h.status != 0)
release(h);
return next;
}
else if (a == 0L || a >= WBIT)
break;
else if (m < RFULL) {
if (U.compareAndSwapLong(this, STATE, s, next = s - RUNIT)) {
if (m == RUNIT && (h = whead) != null && h.status != 0)
release(h);
return next & SBITS;
}
}
else if ((next = tryDecReaderOverflow(s)) != 0L)
return next & SBITS;
}
return 0L;
}
public boolean tryUnlockRead() {
long s, m; WNode h;
while ((m = (s = state) & ABITS) != 0L && m < WBIT) {
if (m < RFULL) {
if (U.compareAndSwapLong(this, STATE, s, s - RUNIT)) {
if (m == RUNIT && (h = whead) != null && h.status != 0)
release(h);
return true;
}
}
else if (tryDecReaderOverflow(s) != 0L)
return true;
}
return false;
}
/** StampedLock锁-尝试释放读锁 */
public boolean tryUnlockRead() {
long s, m; WNode h;
while ((m = (s = state) & ABITS) != 0L && m < WBIT) {
if (m < RFULL) {
if (U.compareAndSwapLong(this, STATE, s, s - RUNIT)) {
if (m == RUNIT && (h = whead) != null && h.status != 0)
release(h);
return true;
}
}
else if (tryDecReaderOverflow(s) != 0L)
return true;
}
return false;
}
}
对于读锁的释放来说,主要提供了以下几种方式:
- unlock() 方法:依据锁的状态status来匹配对应锁的stamp,然后释放锁操作
- unlockRead()方法: 依据锁的状态status来匹配对应读锁的stamp,然后释放锁操作
- tryUnlockRead()方法:释放当前持有的读锁,会设置一个stamp然后返回true,否则,返回false
- tryConvertToReadLock()方法:依据锁的状态status来匹配对应读锁的stamp,然后根据对应情况处理。其中:
- 单写锁模式:一般返回一个对应读锁的stamp
- 悲观读模式:直接返回对应读锁的stamp
- 乐观读模式:需要获取一个读锁,然后是立即返回对应读锁的stamp
- tryConvertToOptimisticRead(): 依据锁的状态status来匹配对应读锁的stamp,然后转换升级处理释放。其中:
- 悲观读模式:属于一般读锁模式,返回的是检测到对应读锁的stamp
- 乐观读模式:需要返回通过验证的对应读锁的stamp
3.4 独占锁WriteLock写锁释放操作实现
/** StampedLock锁-最早在JDK1.8中实现的 */
public class StampedLock implements java.io.Serializable {
/** StampedLock锁-锁释放方法(一般方法) */
public void unlock(long stamp) {
long a = stamp & ABITS, m, s; WNode h;
while (((s = state) & SBITS) == (stamp & SBITS)) {
if ((m = s & ABITS) == 0L)
break;
else if (m == WBIT) {
if (a != m)
break;
state = (s += WBIT) == 0L ? ORIGIN : s;
if ((h = whead) != null && h.status != 0)
release(h);
return;
}
else if (a == 0L || a >= WBIT)
break;
else if (m < RFULL) {
if (U.compareAndSwapLong(this, STATE, s, s - RUNIT)) {
if (m == RUNIT && (h = whead) != null && h.status != 0)
release(h);
return;
}
}
else if (tryDecReaderOverflow(s) != 0L)
return;
}
throw new IllegalMonitorStateException();
}
/** StampedLock锁-写锁释放方法 */
public void unlockWrite(long stamp) {
WNode h;
if (state != stamp || (stamp & WBIT) == 0L)
throw new IllegalMonitorStateException();
state = (stamp += WBIT) == 0L ? ORIGIN : stamp;
if ((h = whead) != null && h.status != 0)
release(h);
}
/** StampedLock锁-写锁转换升级处理并释放锁 */
public long tryConvertToWriteLock(long stamp) {
long a = stamp & ABITS, m, s, next;
while (((s = state) & SBITS) == (stamp & SBITS)) {
if ((m = s & ABITS) == 0L) {
if (a != 0L)
break;
if (U.compareAndSwapLong(this, STATE, s, next = s + WBIT))
return next;
}
else if (m == WBIT) {
if (a != m)
break;
return stamp;
}
else if (m == RUNIT && a != 0L) {
if (U.compareAndSwapLong(this, STATE, s,
next = s - RUNIT + WBIT))
return next;
}
else
break;
}
return 0L;
}
/** StampedLock锁-unlockWrite的核心实现 */
private void release(WNode h) {
if (h != null) {
WNode q; Thread w;
U.compareAndSwapInt(h, WSTATUS, WAITING, 0);
if ((q = h.next) == null || q.status == CANCELLED) {
for (WNode t = wtail; t != null && t != h; t = t.prev)
if (t.status <= 0)
q = t;
}
if (q != null && (w = q.thread) != null)
U.unpark(w);
}
}
}
对于写锁的释放来说,主要提供了以下种方式:
- unlock() 方法:依据锁的状态status来匹配对应锁的stamp,然后释放锁操作
- unlockWrite()方法:依据锁的状态status来匹配对应写锁的stamp,然后释放锁操作
- tryUnlockWrite()方法:释放当前持有的写锁,会设置一个stamp然后返回true,否则,返回false
- tryConvertToWriteLock()方法:依据锁的状态status来匹配stamp,根据对应锁的做升级处理。其中:
- 单写锁模式:直接返回对应的写锁标记stamp
- 读写锁模式:需要释放读锁锁,并返回对应的写锁标记stamp
- 乐观读模式:直接返回对应的写锁标记stamp
综上所述,StampedLock锁本质上依然是一种读写锁,只是没有基于AQS基础同步器来实现,是自定义封装API操作实现的。
写在最后
通过对Java领域中,JDK内部提供的各种锁的实现来看,一直围绕的核心主要还是基于AQS基础同步器来实现的,但是AQS基础同步器不是一种非它不可的技术标准规范,更多的只是一套技术参考指南。
但是,实际上,Java对于锁的实现与运用远远不止这些,还有相位器(Phaser)和交换器(Exchanger),以及在Java JDK1.8版本之前并发容器ConcurrentHashMap中使用的分段锁(Segment)。
不论是何种实现和应用,在Java并发编程领域来讲,都是围绕线程安全问题的角度去考虑的,只是针对于各种各样的业务场景做的具体的实现。
一定意义上来讲,对线程加锁只是并发编程的实现方式之一,相对于实际应用来说,Java领域中的锁都只是一种单一应用的锁,只是给我们掌握Java并发编程提供一种思想没,三言两语也不可能详尽。
到此为止,这算是对于Java领域中并发锁的最终章,文中表述均为个人看法和个人理解,如有不到之处,忘请谅解也请给予批评指正。
最后,技术研究之路任重而道远,愿我们熬的每一个通宵,都撑得起我们想在这条路上走下去的勇气,未来仍然可期,与各位程序编程君共勉!