ReentrantLock源码解析和AQS常见问题分析

news2025/1/18 21:18:49

ReentrantLock和AQS常见问题分析

一、前言

本文利用ReentrantLock作为阅读AQS的切入口,通过问答的方式让大家更好的去理解今天要掌握的点,也欢迎大家说说自己的答案。

二、本文大纲

脑图是个很好的辅助记忆工具,也能提高自己的逻辑思维能力,下文我会通过这个脑图来讲解。
在这里插入图片描述

三、问答环节

  1. 什么是AQS
    AQS是抽象队列同步器,AQS内部维护了一个用volatile修饰的state变量和一个FIFO的双向队列,线程通过CAS修改state,如果CAS失败就将当前线程组装成Node添加到双向队列里面。很多同步工具都继承了AQS类。
  2. AQS能实现哪些同步工具
同步工具同步工具与AQS的关联
ReentrantLock使用AQS保存锁重复持有的次数。
Semaphore使用AQS同步状态来保存信号量的当前计数。
CountDownLatch使用AQS同步状态来表示计数。
ReentrantReadWriteLock使用AQS同步状态中的16位保存写锁持有的次数,剩下的16位用于保存读锁的持有次数。
ThreadPoolExecutorWorker利用AQS同步状态实现对独占线程变量的设置(tryAcquire和tryRelease)。
  1. 你能通过AQS实现一个同步锁吗?
    可以,只需要继承AQS,在重写tryAcquire和tryRelease方法去操作State节点就能实现一个简易的同步锁了。
public class TestMyAQS extends AbstractQueuedSynchronizer {
    @Override
    protected boolean tryAcquire(int arg) {
        while (true){
            if(compareAndSetState(0, arg)){
                return true;
            }
        }
    }

    @Override
    protected boolean tryRelease(int arg) {
        setState(0);
        return true;
    }
}
  1. AQS中为什么要有一个虚拟的head节点
    waitStatus维护了下个节点的状态,可能是挂起也可能是取消,而释放锁的时候需要挑选一个挂起的线程去释放锁,第一个挂起的线程没有前置节点,所以需要创建一个虚拟节点。
  2. AQS中为什么选择使用双向链表,而不是单向链表
    在ReentrantLock中,当调用中断线程排队方法lockInterruptibly时候,会将waitStatus设置成1,从AQS队列中移除,如果是单项链表需要从头开始遍历,很耗时间。
  3. 如果头结点的下一个节点取消了,唤醒节点的时候为什么从后往前找
    从代码可以看到插入的时候node的pred节点能保证一定不为空,如果从头向尾找可能出现调度问题,compareAndSetTail已经成功了,此时pred节点的next节点还是空的。
private Node addWaiter(Node mode) {
        Node node = new Node(Thread.currentThread(), mode);
        // 尝试快速加入到队尾
        Node pred = tail;
        if (pred != null) {
            node.prev = pred;
            if (compareAndSetTail(pred, node)) {
                pred.next = node;
                return node;
            }
        }
        enq(node);
        return node;
    }
  1. ReentrantLock中公平锁和非公平锁有什么区别
  • 公平锁多了一个方法保证拿到的锁的节点都是第一个节点
  • 公平锁相对非公平锁性能要差
  • 公平锁不会出现锁饥饿的问题
  1. 公平锁一定公平吗
    不一定,假设A线程获取到了锁,B线程没获取到锁,正在添加到队列里的过程中,A线程释放锁了,C线程进来了,发现tail==head,于是后来的C获取到了锁。

步骤一、线程B添加队列
注释地方设置tail = head

private Node enq(final Node node) {
        for (;;) {
            Node t = tail;
            if (t == null) { 
                if (compareAndSetHead(new Node()))
                    tail = head; // B线程设置tail = head
            } else {
                node.prev = t;
                if (compareAndSetTail(t, node)) {
                    t.next = node;
                    return t;
                }
            }
        }
    }

步骤二、线程C尝试获取锁
hasQueuedPredecessors,由于h==t,返回false

public final boolean hasQueuedPredecessors() {
      Node t = tail;
      Node h = head;
      Node s;
      return h != t && // 由于h==t,返回false
          ((s = h.next) == null || s.thread != Thread.currentThread());
}

步骤三、线程A释放锁

步骤四、线程C尝试获取锁
CAS成功,线程C获取到了锁

protected final boolean tryAcquire(int acquires) {
            final Thread current = Thread.currentThread();
            int c = getState();
            if (c == 0) {
                if (!hasQueuedPredecessors() &&
                    compareAndSetState(0, acquires)) { // 这里CAS成功,线程C获取到了锁
                    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;
        }
  1. ReentrantLock和Synchronized的区别
  • synchronized是关键字,是JVM层面的底层啥都帮我们做了,而Lock是一个接口,是JDK层面的有丰富的API。
  • synchronized会自动释放锁,而Lock必须手动释放锁。
  • synchronized是不可中断的,Lock可以中断也可以不中断。
  • 通过Lock可以知道线程有没有拿到锁,而synchronized不能。
  • synchronized能锁住方法和代码块,而Lock只能锁住代码块。
  • Lock可以使用读锁提高多线程读效率。
  • synchronized是非公平锁,ReentrantLock可以控制是否是公平锁。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/829192.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

计算机中数据的表示

计算机数据表示 送入计算机的数字&#xff0c;字母&#xff0c;符号等信息必须转换成0、 1组合的数据形式才能被计算机识别。 能够进行算术运算得到明确数值概念的信息成为计算机数值数据&#xff0c;其余的信息成为非数值数据。 从计算机本质的角度而言啊&#xff0c;它所处理…

《零基础入门学习Python》第077讲:Tkinter 模块:标准对话框

Tkinter 为了提供了三种标准对话框模块&#xff0c;它们分别是&#xff1a; messageboxfiledialogcolorchooser 注&#xff1a;这三个模块原来是独立的&#xff0c;分别是 tkMessageBox、tkFileDialog 和 tkColorChooser&#xff0c;需要导入才能使用。在 Python3 之后&#…

http和https的区别?(网络通讯)

HTTP&#xff1a; 超文本传输协议&#xff08;HTTP&#xff0c;HyperText Transfer Protocol&#xff09;是互联网上应用最为广泛的一种 网络协议 HTTPS&#xff1a; 是以安全为目标的 HTTP 通道&#xff0c;是 HTTP 的安全版。HTTPS 的安全基础是 SSL。 两者区别: 1、HTTPS …

Vue3_02 创建Vue3.0工程

1.使用 vue-cli 创建 ## 查看 vue/cli 版本&#xff0c;确保 vue/cli 版本在4.5.0以上 vue -V 或 vue --version## 安装或升级你的 vue/cli npm install -g vue/cli## 创建 vue create vue_test## 启动 cd vue-test npm run serve 2.使用 vite 创建 什么是vite?——新一代…

Golang之路---03 面向对象——接口与多态

接口与多态 何为接口 在面向对象的领域里&#xff0c;接口一般这样定义&#xff1a;接口定义一个对象的行为。接口只指定了对象应该做什么&#xff0c;至于如何实现这个行为&#xff08;即实现细节&#xff09;&#xff0c;则由对象本身去确定。   在 Go 语言中&#xff0c;…

24考研数据结构-二叉树的遍历

目录 5.3二叉树的遍历和线索二叉树数据结构&#xff1a;树的遍历前序遍历中序遍历后序遍历层次遍历遍历方法的选择结论 树的遍历应用5.3.1二叉树的遍历1. 先序遍历&#xff08;根左右 NLR&#xff09;2. 中序遍历&#xff08;左根右 LNR&#xff09;3. 后续遍历&#xff08;左右…

基于Jenkins+Python+Ubuntu+Docker的接口/UI自动化测试环境部署详细过程

基于JenkinsPythonUbuntuDocker的接口/UI自动化测试环境部署详细过程 1 Jenkins是什么&#xff1f;2 Jenkins目标是什么&#xff1f;3 什么是CI/CD?3.1 CI持续集成3.2 CD持续部署3.3 CD持续交付 4 Ubuntu环境4.1 环境需求4.2 实现思路 5 Ubuntu下安装Docker6 安装Jenkins6.1 拉…

电力巡检无人机助力迎峰度夏,保障夏季电力供应

夏季是电力需求量较高的时期&#xff0c;随着高温天气的来临&#xff0c;风扇、空调和冰箱等电器的使用量也大大增加&#xff0c;从而迎来夏季用电高峰期&#xff0c;电网用电负荷不断攀升。为了保障夏季电网供电稳定&#xff0c;供电公司会加强对电力设施设备的巡检&#xff0…

新抗原预测的计算工作流程

参考文献&#xff1a;Xie N, Shen G, Gao W, Huang Z, Huang C, Fu L. Neoantigens: promising targets for cancer therapy. Signal Transduct Target Ther. 2023 Jan 6;8(1):9. doi: 10.1038/s41392-022-01270-x. PMID: 36604431; PMCID: PMC9816309. 文章目录 *新抗原预测的…

【腾讯云 Cloud Studio 实战训练营】使用Cloud Studio快速构建React完成点餐H5页面还原

【腾讯云 Cloud Studio 实战训练营】使用Cloud Studio快速构建React完成点餐H5页面还原 一、前言二、Cloud Studio 简介1.Cloud Studio 是什么2.Cloud Studio 的优点 三、Cloud Studio 登录注册四、Cloud Studio 的初体验五、使用 Cloud Studio 开发一个简版的点餐系统1.安装 a…

开源元数据管理平台Datahub最新版本0.10.5——安装部署手册(附离线安装包)

大家好&#xff0c;我是独孤风。 开源元数据管理平台Datahub近期得到了飞速的发展。已经更新到了0.10.5的版本&#xff0c;来咨询我的小伙伴也越来越多&#xff0c;特别是安装过程有很多问题。本文经过和群里大伙伴的共同讨论&#xff0c;总结出安装部署Datahub最新版本的部署手…

SpringCloud深入理解 | 生产者、消费者

&#x1f497;wei_shuo的个人主页 &#x1f4ab;wei_shuo的学习社区 &#x1f310;Hello World &#xff01; SpringCloud Spring Cloud是一组用于构建分布式系统和微服务架构的开源框架和工具集合。它是在Spring生态系统的基础上构建的&#xff0c;旨在简化开发人员构建分布式…

Tomcat添加第三方jar包、如何在IDEA中启动部署Web模板

前言:公司最近维护老项目,是最原始的web项目,servlet和jsp结合的web项目,启动的时候配置了好几遍, 都起不来,很折磨人,这个文档比较全配置一遍准备工作 首先 拉取代码: git clone xxx.git ,如需要别的操作,自行baidu 也可以在idea中拉取第一步File ->Project Structure->…

张驰咨询:精益生产管理的独特特点和显著优势

精益生产管理是一种注重效率和优化生产流程的管理方法&#xff0c;旨在通过最小化浪费和提高生产效率来降低成本和提高产品质量。以下是精益生产管理的主要特点&#xff1a; 优化生产流程 精益生产管理强调通过优化生产流程来提高效率和降低成本。这包括通过标准化工作流程、…

【数据结构与算法】TypeScript 实现图结构

class Grapg<T> {// 用于存储所有的顶点verteces: T[] [];// 用于存储所有的边 采用邻接表的形式adjList: Map<T, T[]> new Map();// 添加顶点addVertex(v: T) {this.verteces.push(v);// 初始化顶点的邻接表this.adjList.set(v, []);}// 添加边addEdge(v: T, w:…

通向架构师的道路之Tomcat性能调优

一、总结前一天的学习 从“第三天”的性能测试一节中&#xff0c;我们得知了决定性能测试的几个重要指标&#xff0c;它们是&#xff1a; 吞吐量 Responsetime Cpuload MemoryUsage 我 们也在第三天的学习中对Apache做过了一定的优化&#xff0c;使其最优化上…

计算机视觉与图形学-神经渲染专题-ConsistentNeRF

摘要 Neural Radiance Fields (NeRF) 已通过密集视图图像展示了卓越的 3D 重建能力。然而&#xff0c;在稀疏视图设置下&#xff0c;其性能显着恶化。我们观察到&#xff0c;在这种情况下&#xff0c;学习不同视图之间像素的 3D 一致性对于提高重建质量至关重要。在本文中&…

总结了12句话,送给通信新员工

1、入职第一件事——改变形象。 刚毕业的大学生进入工作单位之后&#xff0c;需要尽快完成身份的转换——从一名学生&#xff0c;变身为一个职业人。 这个转换的第一步&#xff0c;就是改变形象外表。 大学里过于随意的穿搭&#xff0c;请一定不要带进单位&#xff0c;尤其是入…

Flowable-子流程-调用活动

目录 定义图形标记XML内容界面操作使用示例子流程设计子流程的XML内容主流程设计主流程的XML内容 视频教程 定义 调用活动是在一个流程定义中调用另一个独立的流程定义&#xff0c;通常可以定义一些通用的流程作为 这种调用子流程&#xff0c;供其他多个流程定义复用。这种子流…

✅1本期刊发生变动,EI期刊目录更新!

【SciencePub学术】继上次更新后一个月&#xff0c;爱思唯尔&#xff08;Elsevier&#xff09;官网更新了EI Compendex收录期刊目录。本次更新EI期刊目录中&#xff0c;Serials&#xff08;连续出版/核心收录&#xff09;列表共收录期刊名称5347个&#xff0c;与上次更新相比&a…