ReentrantLock 原理解析(中)

本文涉及的产品
云解析 DNS,旗舰版 1个月
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
全局流量管理 GTM,标准版 1个月
简介: ReentrantLock 原理解析

非公平锁 lock 方法实现


image.png


特别说明一下:下面会涉及到非公平/公平锁的解析,在解析之前我会加粗说是那种方式的锁。


acquire()


acquire 方法在 AbstractQueuedSynchronizer 类中,该方法就是去尝试获取锁,如果获取失败了,就会尝试再次获取,或者进入 AQS 队列中。


public final void acquire(int arg) {
    if (!tryAcquire(arg) &&
        acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
        selfInterrupt();
}


主要有三条流程(本质就是 3 个核心方法) tryAcquireaddWaiteracquireQueued


FairSync#tryAcquire


1、公平锁tryAcquire 是由子类 FairSync 实现, 这里和非公平锁的区别就在于调用了 hasQueuedPredecessors 方法判断是否有线程在排队。


protected final boolean tryAcquire(int acquires) {
    final Thread current = Thread.currentThread();
    int c = getState();
    if (c == 0) {
        if (
            // hasQueuedPredecessors 这里就是公平锁的体现,如果人在排队就不能获取锁,需要先进入排队,所以性能上来说比非公平锁要一些。
            !hasQueuedPredecessors() &&
            // cas 获取锁
            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;
}


2、addWaiter 操作,顾名思义就是当前线程尝试获取锁失败,然后进入将当前 Thread 封装为一个 AQS Node 节点,进入等待队列排队


private Node addWaiter(Node mode) {
    Node node = new Node(Thread.currentThread(), mode);
    // Try the fast path of enq; backup to full enq on failure
    Node pred = tail;
    if (pred != null) {
        node.prev = pred;
        if (compareAndSetTail(pred, node)) {
            pred.next = node;
            // 后续通过这里返回
            return node;
        }
    }
    // 首次进入
    enq(node);
    return node;
}


如果没有尾节点会调用 enq进行初始化头节点和入队操作:


private Node enq(final Node node) {
    for (;;) {
        Node t = tail;
        // 首次初始化
        if (t == null) { // Must initialize
            if (compareAndSetHead(new Node()))
                tail = head;
        } else {
            // 加入当前节点
            node.prev = t;
            if (compareAndSetTail(t, node)) {
                t.next = node;
                return t;
            }
        }
    }
}


3、调用acquireQueued 方法是在,当前获取锁的线程进入 AQS 之后进入阻塞之前执行的方法,其实就是在进入等待之前做最后一次 “抢救” 尝试能否获取锁。还有一个逻辑就是在 AQS 唤醒过后,当前获取到锁的节点就成了头节点,会将 "旧" 头节点丢弃掉。


final boolean acquireQueued(final Node node, int arg) {
    boolean failed = true;
    try {
        boolean interrupted = false;
        for (;;) {
            final Node p = node.predecessor();
            if (p == head && tryAcquire(arg)) {
                setHead(node);
                p.next = null; // help GC
                failed = false;
                return interrupted;
            }
            if (shouldParkAfterFailedAcquire(p, node) &&
                parkAndCheckInterrupt())
                interrupted = true;
        }
    } finally {
        if (failed)
            cancelAcquire(node);
    }
}


NonfairSync#tryAcquire()


1、 本次走非公平锁 NonfairSync, 非公平锁对比公平锁没有是否有队列的排队的判断,只要是锁获取的参与者,都可以直接进行锁的竞争。


// 首先我们先看 `tryAcquire()` 方法
protected final boolean tryAcquire(int acquires) {
    return nonfairTryAcquire(acquires);
}
// 核心是调用 `nonfairTryAcquire(acquires)` 
final boolean nonfairTryAcquire(int acquires) {
    final Thread current = Thread.currentThread();
    int c = getState();
    // 如果可以获取锁
    if (c == 0) {
        // 修改 state 
        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;
}


2、 addWaiter(Node.EXCLUSIVE)addWaiter方法实现


image.png


如果前面没有排队线程将调用 enq方法


image.png


双向链表中,第一个节点为虚节点(也叫做哨兵节点),其实并不存储任何信息,只是占位,真正的第一个有数据的节点,是从第二个节点开始的。


acquireQueued(addWaiter(Node.EXCLUSIVE))


final boolean acquireQueued(final Node node, int arg) {
    boolean failed = true;
    try {
        boolean interrupted = false;
        // 循环
        for (;;) { 
            final Node p = node.predecessor();
            if (p == head && tryAcquire(arg)) {
                // 哨兵节点出队
                setHead(node);
                p.next = null; // help GC
                failed = false;
                return interrupted;
            }
            // shouldParkAfterFailedAcquire 第 1 次返回 false
            // 第 2 次返回 false
            if (shouldParkAfterFailedAcquire(p, node) &&
                // 内部会阻塞线程调用 LockSupport.park(this);
                parkAndCheckInterrupt())
                interrupted = true;
        }
    } finally {
        if (failed)
            cancelAcquire(node);
    }
}
// setHead 方法
// 就是把当前获取锁的节点设置为哨兵节点,然后之前旧哨兵节点设置为 null
private void setHead(Node node) {
    head = node;
    node.thread = null;
    node.prev = null;
}


相关文章
|
1月前
|
存储 算法 Java
解析HashSet的工作原理,揭示Set如何利用哈希算法和equals()方法确保元素唯一性,并通过示例代码展示了其“无重复”特性的具体应用
在Java中,Set接口以其独特的“无重复”特性脱颖而出。本文通过解析HashSet的工作原理,揭示Set如何利用哈希算法和equals()方法确保元素唯一性,并通过示例代码展示了其“无重复”特性的具体应用。
41 3
|
22天前
|
算法 Java 数据库连接
Java连接池技术,从基础概念出发,解析了连接池的工作原理及其重要性
本文详细介绍了Java连接池技术,从基础概念出发,解析了连接池的工作原理及其重要性。连接池通过复用数据库连接,显著提升了应用的性能和稳定性。文章还展示了使用HikariCP连接池的示例代码,帮助读者更好地理解和应用这一技术。
36 1
|
2天前
|
JavaScript 前端开发 API
Vue.js响应式原理深度解析:从Vue 2到Vue 3的演进
Vue.js响应式原理深度解析:从Vue 2到Vue 3的演进
18 0
|
27天前
|
数据采集 存储 编解码
一份简明的 Base64 原理解析
Base64 编码器的原理,其实很简单,花一点点时间学会它,你就又消除了一个知识盲点。
68 3
|
8天前
|
API 持续交付 网络架构
深入解析微服务架构:原理、优势与实践
深入解析微服务架构:原理、优势与实践
12 0
|
9天前
|
存储 供应链 物联网
深入解析区块链技术的核心原理与应用前景
深入解析区块链技术的核心原理与应用前景
|
9天前
|
存储 供应链 安全
深度解析区块链技术的核心原理与应用前景
深度解析区块链技术的核心原理与应用前景
18 0
|
24天前
|
供应链 安全 分布式数据库
探索区块链技术:从原理到应用的全面解析
【10月更文挑战第22天】 本文旨在深入浅出地探讨区块链技术,一种近年来引起广泛关注的分布式账本技术。我们将从区块链的基本概念入手,逐步深入到其工作原理、关键技术特点以及在金融、供应链管理等多个领域的实际应用案例。通过这篇文章,读者不仅能够理解区块链技术的核心价值和潜力,还能获得关于如何评估和选择适合自己需求的区块链解决方案的实用建议。
44 0
|
1月前
|
前端开发 JavaScript UED
axios取消请求CancelToken的原理解析及用法示例
axios取消请求CancelToken的原理解析及用法示例
103 0
|
1月前
|
存储 缓存 数据处理
深度解析:Hologres分布式存储引擎设计原理及其优化策略
【10月更文挑战第9天】在大数据时代,数据的规模和复杂性不断增加,这对数据库系统提出了更高的要求。传统的单机数据库难以应对海量数据处理的需求,而分布式数据库通过水平扩展提供了更好的解决方案。阿里云推出的Hologres是一个实时交互式分析服务,它结合了OLAP(在线分析处理)与OLTP(在线事务处理)的优势,能够在大规模数据集上提供低延迟的数据查询能力。本文将深入探讨Hologres分布式存储引擎的设计原理,并介绍一些关键的优化策略。
104 0

推荐镜像

更多
下一篇
无影云桌面