JAVA高并发——锁的优化

文章目录

  • 1、减少锁持有时间
  • 2、减小锁粒度
  • 3、用读写分离锁来替换独占锁
  • 4、锁分离
  • 5、锁粗化

锁是最常用的同步方法之一。在高并发的环境下,激烈的锁竞争会导致程序的性能下降,因此我们有必要讨论一些有关锁的性能的问题,以及一些注意事项,比如避免死锁、减小锁粒度、锁分离等。

在多核时代,使用多线程可以明显地提高系统的性能,但是也会额外增加系统的开销。

对于单任务或者单线程的应用而言,其主要资源消耗都源自任务本身。它既不需要维护并行数据结构间的一致性状态,也不需要为线程的切换和调度花费时间。但对于多线程应用来说,系统除了处理功能需求,还需要额外维护多线程环境的特有信息,如线程本身的元数据、线程的调度和线程上下文的切换等。

事实上,在单核CPU上采用并行算法的效率一般要低于原始的串行算法的效率,并行计算之所以能提高系统的性能,并不是因为它“少干活”了,而是因为它可以更合理地进行任务调度,充分利用各个CPU的资源。因此,只有合理设计并发,才能将多核CPU的性能发挥到极致。

1、减少锁持有时间

对于使用锁进行并发控制的应用程序而言,在锁竞争过程中,单个线程对锁的持有时间与系统性能有着直接关系。线程持有锁的时间越长,那么相对地,锁的竞争程度也就越激烈。可以想象一下,如果要求100个人各自填写自己的身份信息,但是只给他们一支笔,那么如果每个人拿持笔的时间都很长,总体所花的时间就会很长。如果真的只有一支笔共享给100个人用,那么最好让每个人花尽量少的时间持笔,务必做到想好了再落笔,千万不能拿着笔才去思考信息应该怎么填。程序开发也是类似的,应该尽可能地减少对某个锁的持有时间,以降低线程间互斥的可能性。以下面的代码段为例:
在这里插入图片描述
在syncMethod()方法中,假设只有mutextMethod()方法是有同步需要的,而othercode1()方法和othercode2()方法并不需要做同步控制。如果othercode1()方法和othercode2()方法分别是重量级的方法,则会花费较长的CPU时间。如果在并发量较大时,使用这种对整个方法做同步的方案,则会导致等待线程大量增加。因为一个线程在进入该方法时获得内部锁,只有在所有任务都执行完后,才会释放锁。

一个较为优化的解决方案是,只在必要时进行同步,这样就能明显减少线程持有锁的时间,提高系统的吞吐量。
在这里插入图片描述
改进的代码只针对mutextMethod()方法做了同步,锁占用的时间相对较短,因此能有更高的并行度。这种技术手段在JDK的源码包中也很容易见到,比如处理正则表达式的Pattern类。
在这里插入图片描述
matcher()方法有条件地进行锁请求,只有在表达式未编译时,进行局部加锁。这种处理方式大大提高了matcher()方法的执行效率和可靠性。

注意:减少锁持有时间有助于降低锁冲突的可能性,进而提升系统的并发能力。

2、减小锁粒度

减小锁粒度也是一种削弱多线程锁竞争的有效手段。这种技术的典型使用场景就是ConcurrentHashMap类的实现。对于一个线程安全的HashMap而言,在进行并发put()操作时,锁住整个Map自然是最简单也是最安全的一种实现,然而这种简单的实现却有着极其糟糕的并发性能。试想,如果在操作过程中,可以锁住局部而非整体,是不是可以既做到线程安全,又提高并行度呢?实际上,当ConcurrentHashMap插入元素时,它并不会锁住整个HashMap,而是仅仅在要操作的那个Node上加锁,因此在同一时刻,可以有多个不同的线程同时对Map进行操作而不产生数据冲突。它的伪代码如下:
在这里插入图片描述
注意:所谓减小锁粒度,就是缩小锁定对象的范围,从而降低锁冲突的可能性,进而提高系统的并发能力。

3、用读写分离锁来替换独占锁

使用读写分离锁ReadWriteLock可以提高系统的性能。使用读写分离锁来替代独占锁是减小锁粒度的一种特殊情况。如果说减小锁粒度是通过分割数据结构实现的,那么读写分离锁则是对系统功能点的分割。

在读多写少的场合,读写锁对系统性能提升是很有好处的。因为如果系统在读写数据时均只使用独占锁,那么读操作和写操作间、读操作和读操作间、写操作和写操作间均不能实现真正的并发,并且需要相互等待。而读操作本身不会影响数据的完整性和一致性,因此,从理论上讲,在大部分情况下,可以允许多线程同时读,读写锁便是实现这种功能的。

注意:在读多写少的场合使用读写锁可以有效提升系统的并发能力。

4、锁分离

如果将读写锁的思想进一步延伸,就是锁分离。根据读写操作功能上的不同,我们对读写锁进行了有效的锁分离。依据应用程序的功能特点,使用类似的分离思想,也可以对独占锁进行分离。一个典型的案例就是java.util.concurrent.LinkedBlockingQueue的实现。

在LinkedBlockingQueue的实现中,take()方法和put()方法分别实现了从队列中取得数据和向队列中添加数据的功能。虽然两个方法都对当前队列进行了修改,但由于LinkedBlockingQueue是基于链表的,因此两个操作分别作用于队列的头部和尾部,从理论上说,两者并不冲突。

如果使用独占锁,则要求在两个操作进行时获取当前队列的独占锁,那么take()方法和put()方法就不可能真正并发执行,在运行时,它们会彼此等待对方释放锁资源。在这种情况下,锁竞争会相对比较激烈,从而影响程序在高并发时的性能。

因此,在JDK的实现中并没有采用这样的方式,取而代之的是用两把不同的锁分离take()方法和put()方法:
在这里插入图片描述
以上代码片段定义了takeLock和putLock,它们分别在take()方法和put()方法中使用。因此,take()方法和put()方法就此相互独立,它们之间不存在锁竞争关系,只在take()方法和take()方法间、put()方法和put()方法间存在对takeLock和putLock的竞争,从而削弱了锁竞争的可能性。

take()方法的实现如下,笔者在代码中给出了详细的注释,故不在正文中做进一步说明了:
在这里插入图片描述
put()方法的实现如下:
在这里插入图片描述
通过takeLock和putLock两把锁,LinkedBlockingQueue实现了读数据和写数据的分离,使两者在真正意义上成为可并发的操作。

5、锁粗化

通常情况下,为了保证多线程间的有效并发,会要求每个线程持有锁的时间尽量短,即在使用完公共资源后,应该立即释放锁。只有这样,等待在这个锁上的其他线程才能尽早地获得资源去执行任务。但是,凡事都有一个度,如果对同一个锁不停地进行请求、同步和释放,其本身也会消耗系统宝贵的资源,反而不利于性能的优化。

为此,虚拟机在遇到一连串连续地对同一个锁不断进行请求和释放的操作时,便会把所有的锁操作整合成对锁的一次请求,从而减少对锁的请求同步的次数,这个操作叫作锁粗化。示例如下:
在这里插入图片描述
上面的代码段会被整合成如下形式:
在这里插入图片描述
在开发过程中,大家也应该有意识地在合理的场合进行锁粗化,尤其是在循环内请求锁时。以下是一个在循环内请求锁的例子,在这种情况下,意味着每次循环中都有请求锁和释放锁的操作,但显然是没有必要的:
在这里插入图片描述
所以,一种更加合理的做法应该是只在外层请求一次锁:
在这里插入图片描述

**注意:**性能优化就是根据运行时的真实情况对各个资源点进行权衡折中的过程。锁粗化的思想和减少锁持有时间是相反的,在不同的场合,它们的效果不同,因此要根据实际情况进行取舍。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

vulhub中Apache Log4j Server 反序列化命令执行漏洞复现(CVE-2017-5645)

Apache Log4j是一个用于Java的日志记录库,其支持启动远程日志服务器。Apache Log4j 2.8.2之前的2.x版本中存在安全漏洞。攻击者可利用该漏洞执行任意代码。 1.我们使用ysoserial生成payload,然后直接发送给your-ip:4712端口即可。 java -jar ysoserial-…

微软和OpenAI将检查AI聊天记录,以寻找恶意账户

据国外媒体报道,大型科技公司及其附属的网络安全、人工智能产品很可能会推出类似的安全研究,尽管这会引起用户极度地隐私担忧。大型语言模型被要求提供情报机构信息,并用于帮助修复脚本错误和开发代码以侵入系统,这将很可能会成为…

01 Qt自定义风格控件的基本原则

目录 1.继承原生控件 2.组合原生控件 3.仿写原生控件 PS:后续将继续分享开发实践中各类自定义控件的方法、思路以及组件库 1.继承原生控件 关键字:继承、paintEvent 这里想说的是,Qt的Gui框架在封装原生控件的同时, 也为开发者提供了各…

AGI|一篇小白都能看懂的RAG入门介绍!

目录 一、前言 二、LLM主要存在的问题 三、RAG 是什么? 四、RAG中的搜索器 (一)主要的检索技术 (二)知识库索引技术 五、RAG目前遇到的问题和展望 一、前言 随着近几年AIGC的发展,不仅是大模型自身在…

Web3探索加密世界:什么是空投?

随着加密货币行业的迅速发展,人们开始听说各种各样的术语,其中包括"空投"(Airdrop)。在这里,我们将深入探讨什么是空投,以及它在加密世界中扮演的角色。 什么是空投? 空投是指在加密…

2.18号c++

1.菱形继承 1.1 概念 菱形继承又称为钻石继承,是由公共基类派生出多个中间子类,又由多个中间子类共同派生出汇聚子类。汇聚子类会得到多份中间子类从公共基类继承下来的数据成员,会造成空间浪费,没有必要。 问题: …

使用 GitHub Codespaces 加速 Elixir 开发环境工作速度

文章目录 前言加速 Elixir 开发环境参考 前言 使用 Elixir 开发点小玩意的时候,面对经常需要走外网下载依赖 (Elixir 的镜像站 UPYUN 使用有时候也经常抽风) 的时候,为了避免需要不断的进行网络代理配置,有想到之前经常使用 GitHub Codespac…

Spring两大核心思想:IOC和AOP

目录 IOC:控制反转 Ioc概念 Ioc的优点 Spring Ioc AOP:面向切面编程 AOP的优点 Spring AOP 1.添加依赖 2.核心概念 3.通知的类型 4.切点表达式 5.公共切点 pointCut 6.切面优先级 Order 7.使用自定义注解完成AOP的开发 Spring AOP实现有几种方式? S…

【C++】类与对象(构造函数、析构函数、拷贝构造函数、常引用)

🌈个人主页:秦jh__https://blog.csdn.net/qinjh_?spm1010.2135.3001.5343🔥 系列专栏:http://t.csdnimg.cn/eCa5z 目录 类的6个默认成员函数 构造函数 特性 析构函数 特性 析构的顺序 拷贝构造函数 特性 常引用 前言 &…

家用电器液位开关有哪些

在当代家庭中,随着科技的进步和生活水平的提升,各种智能化家用电器成为家庭生活的重要组成部分。这些家用电器如空调、除湿机、加湿器、洗碗机、净水器等,在运作过程中往往需要对液体的高低位进行精确控制,以保证其正常、高效的运…

SpringBoot + Nacos 实现动态化线程池

1.背景 在后台开发中,会经常用到线程池技术,对于线程池核心参数的配置很大程度上依靠经验。然而,由于系统运行过程中存在的不确定性,我们很难一劳永逸地规划一个合理的线程池参数。 在对线程池配置参数进行调整时,一…

BioTech - 交联质谱 (Crosslinks) 的常见数据格式说明

欢迎关注我的CSDN:https://spike.blog.csdn.net/ 本文地址:https://spike.blog.csdn.net/article/details/136190750 交联质谱 (Crosslinking Mass Spectrometry,Crosslinks) 技术 是一种结合化学交联剂和质谱仪的方法,用于研究蛋…

第十篇:node处理404和服务器错误

🎬 江城开朗的豌豆:个人主页 🔥 个人专栏 :《 VUE 》 《 javaScript 》 📝 个人网站 :《 江城开朗的豌豆🫛 》 ⛺️ 生活的理想,就是为了理想的生活 ! 目录</

Vue3+vite搭建基础架构(10)--- 使用less和vite-plugin-vue-setup-extend

Vue3vite搭建基础架构&#xff08;10&#xff09;--- 使用less和vite-plugin-vue-setup-extend 说明官方文档安装less测试less表达式安装vite-plugin-vue-setup-extend 说明 这里记录下自己在Vue3vite的项目使用less来写样式以及使用vite-plugin-vue-setup-extend直接定义组件…

【8】知识加工

一、概述 对信息抽取/知识融合后得到的“事实”进行知识推理以拓展现有知识、得到新知识。 知识加工主要包括三方面内容&#xff1a;本体构建、知识推理和质量评估。 二、本体构建 1.本体 定义&#xff1a;本体是用于描述一个领域的术语集合&#xff0c;其组织结构是层次结…

设计模式四:适配器模式

1、适配器模式的理解 适配器模式可以理解为有两个现成的类Adaptee和Target&#xff0c;它们两个是不能动的&#xff0c;要求必须使用B这个类来实现一个功能&#xff0c;但是A的内容是能复用的&#xff0c;这个时候我们需要编写一个转换器 适配器模式 Adaptee&#xff1a;被适…

Java Web(一)--介绍

Java Web 技术体系图 三大组成部分&#xff1a; 前端&#xff1a; 前端开发技术工具包括三要素&#xff1a;HTML、CSS 和 JavaScript&#xff1b;其他高级的前端框架&#xff0c;如bootstrap、jquery&#xff0c;VUE 等。 后端&#xff1a; 后端开发技术工具主要有&am…

剑指offer刷题笔记-链表

少年何妨梦摘星 敢挽桑弓射玉衡 解决与链表相关的问题总是有大量的指针操作&#xff0c;而指针操作的代码总是容易出错的。很多面试官喜欢出与链表相关的问题&#xff0c;就是想通过指针操作来考察应聘者的编码功底。 题目链接来自于 AcWing 、Leetcode&#xff08;LCR&#xf…

燃气企业须知 :智慧燃气信息化管理平台的作用 ?

关键词&#xff1a;智慧燃气、燃气信息化、智慧燃气平台、智慧燃气建设、智慧燃气解决方案 智慧燃气信息化管理平台通过城市输气管网作为主要基础&#xff0c;各个终端用户进行协调&#xff0c;依托信息化数据进行管理&#xff0c;从而让整个工作环节流程达到高效智能优势。通…

【LeetCode】树的BFS(层序遍历)精选6题

目录 1. N 叉树的层序遍历&#xff08;中等&#xff09; 2. 二叉树的锯齿形层序遍历&#xff08;中等&#xff09; 3. 二叉树的最大宽度&#xff08;中等&#xff09; 4. 在每个树行中找最大值&#xff08;中等&#xff09; 5. 找树左下角的值&#xff08;中等&#xff09…