乐趣区

关于java:Java并发编程解析-基于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 畛域中并发锁的最终章,文中表述均为集体认识和集体了解,如有不到之处,忘请谅解也请给予批评指正。

最初,技术钻研之路任重而道远,愿咱们熬的每一个通宵,都撑得起咱们想在这条路上走上来的勇气,将来依然可期,与各位程序编程君共勉!

退出移动版