Synchronized锁原理及 ConcurrentHashMap

news2024/11/20 2:40:52

在这里插入图片描述

文章目录

  • 一、Synchronized原理
    • 加锁过程
    • 锁消除
    • 锁粗化
  • 二、线程安全的集合类
    • 多线程环境使用ArrayList
    • 多线程环境使用队列
    • 多线程环境下使用哈希表

一、Synchronized原理

我们表面看到的,两个线程针对同一对象加锁,就会产生阻塞等待,但实际我们的Synchronized有许多特性,以及内存实现的一些优化机制。

1.刚开始的时候是乐观锁,如果锁冲突较大,就转换为悲观锁
2.刚开始是轻量级锁,如果锁被占有的时间较长,就转换为重量级锁
3.实现轻量级锁大概率会用到自旋锁策略
4.是一种可重入锁
5.是一种不公平锁
6.不是读写锁

加锁过程

我们的JVM将Synchronized锁分为以下状态: 无锁,偏向锁,轻量级锁,重量级锁,会根据锁冲突的情况,依次进行升级。
在这里插入图片描述
当我们使用Synchronized的时候,我们优先会进入偏向锁的状态。

偏向锁: 偏向锁并不是真正的加锁,只是在对象头中做一个标记(并没有加锁,这个操作比加锁轻许多),如果在执行代码过程中,并没有其他线程来尝试加锁,那么在执行完Synchronized之后,取消偏向锁。如果有其他线程来尝试加锁,我们会将偏向锁升级为轻量级锁。
我们的Synchronized采取偏向锁这一操作也很好理解,加锁是有一定开销的,我们能不加就不加,但是标记还是得做,用来判断合适升级为轻量级锁。

轻量级锁:随时锁竞争,我们的Synchronized从偏向锁转为轻量级锁(自旋锁),基于CAS实现的。
如果其他的线程很快的释放锁,那么我们的自旋锁是非常合适的,因为并没有阻塞等待,而且一直尝试获取锁,但是如果其他线程长时间占用锁,那么我们自旋是十分的浪费CPU资源的,大概率我们Synchronized在自旋过程中,内部有一个计数器来记录我们自旋了多少次,自旋达到一次的次数我们就会升级为重量级锁。
重量级锁(挂起等待锁): 当我们自旋不能快速获取到锁时,就会升级为重量级锁。
这里会使用到我们内核提供的互斥锁mutex 的一组API,操作系统内核提供的加锁功能,如果我们发生了锁竞争,我们的线程就会被放到阻塞队列中,不参与CPU调度,当锁被释放了之后,线程才有机会被调度,因为调度是随机的,所以它有机会获取到锁。

锁消除

这个也很好理解,在一些不需要加锁的场景下,我们加锁了,编译器和JVM会判断我们的锁是否可以消除,如果可以消除,直接就消除了。
比如我们String提供了StringBuffer和StringBuilder,StringBuffer是线程安全的,在方法中加了Synchronized,但是当我们在单线程操作下,不会涉及到线程安全问题,所以我们编译器会直接将锁消除掉。

锁粗化

锁的粒度: Synchronized代码块所包含代码越多,锁的粒度越粗,包含的代码越少,锁的粒度越细。
正常情况下,我们希望锁的粒度越细越好,因为我们加锁的代码是不能够并发执行的。
在这里插入图片描述
但有一些情况下,我们锁的粒度粗一点越好。
我们两次加锁解锁的时间间隙非常小,分开加锁会造成额外的加锁解锁的时间开销,而且中间间隙很小,就算并发效果也不是很明显,这里还不如直接搞一把大锁。
这个举一个例子:
我们过年在房间吃花生,会剥皮,但是垃圾桶在客厅,我们现在有两种方案,第一种,剥一个花生,就去客厅扔一下皮,第二种,我们先把皮放在卫生纸上,等积攒一部分再去客厅扔,我们扔皮这个操作就相当于加锁。

二、线程安全的集合类

我们的集合类中,大部分都是不安全的,也有一些是安全的但是不建议使用,比如: Vector,Stack,HashTable,他们的关键方法都带有Synchronized。
我们举例说明如果在多线程环境下,使用线程不安全的集合。

多线程环境使用ArrayList

1.在我们认为会产生线程安全问题的地方加锁,Synchronized或者ReentrantLock都可以。
2.使用Collections.synchronizedList(new Arraylist),synchronizedList的关键方法都加了Synchronized.
3.使用CopyOnWritArrayList: CopyOnWrit (COW 简称:写时拷贝),简单的来说如果我们针对该ArrayList进行读操作,那么我们不做任何操作。如果我们进行写操作,那么我们就拷贝一份ArrayList,然后对新的进行写操作,如果在修改过程中有读操作,那么就去读旧ArrayList,当我们新的ArrayList写完了之后,让旧的指向新的(这个指向的操作相当于引用的赋值是原子的).

我们的COW的操作:
优点: 在读多写少的情况下,因为没有加锁操作,所以效率很高
缺点: 1. 如果我们的ArrayList存放的元素非常的多,那么我们会占用大量的内存空间
2. 我们的新写的数据不能被第一时间读取到。

多线程环境使用队列

队列作用
ArrayBlockingQueue基于数组实现的阻塞队列
LinkedBlockingQueue基于链表实现的阻塞队列
PriorityBlockingQueue基于堆实现的带优先级的阻塞队列
TransferQueue最多只包含一个元素的阻塞队列

多线程环境下使用哈希表

我们的HashMap是线程不安全的,Hashtable是线程安全的,但是它直接在方法上加了Synchronized。
在这里插入图片描述
java为我们提供了优化了的线程安全的哈希表:ConcurrentHashMap
这里我们需要着重了解: ConcurrentHashMap与Hashtable的区别是什么?

1.最大的优化:ConcurrentHashMap 相比于 Hashtable大大缩小了锁冲突的概率,将一把大锁准换为多把小锁。
在这里插入图片描述
我们的哈希表中,元素1,2是在同一条链表上,如果线程一去操作(修改,删除等)元素1,线程二去操作(修改,删除等)元素2,是否有线程安全问题?
如果我们的1,2是相邻的,如果我们进行了插入或者删除操作,那么这两结点的next指向就会发上改变。

如果我们的线程一去操作元素3,线程二去操作元素4,存在线程安全问题吗?
因为我们的元素3和元素4,是存于不同的链表中,相当于我们的多线程同时修改不同的变量,是不存在线程安全问题的,所以这个情况我们情况不需要加锁的。

我们的Hashtable是直接加了一把大锁,相当于只要两个线程去操作哈希表,即使是不同链表上的元素,也是不允许的。
在这里插入图片描述
ConcurrentHashMap为我们每一条链表提供了一把锁,每个链表的头结点作为锁对象,大大降低了锁冲突的概率。
在这里插入图片描述
我们锁的粒度变小了。
当我们不同线程操作1 2 时,是针对同一把锁,会产生锁竞争,保证线程安全。
当我们不同线程操作3 4 时,是针对不同的锁进行加锁,所以不会产生锁竞争。
我们的JDK1.8之前,ConcurrentHashMap使用的是分段锁
在这里插入图片描述
我们的分段锁的本质也是缩小锁的范围,来降低锁冲突的概率,但是不够彻底,粒度还是不够细,而且实现起来也更麻烦。

2. ConcurrentHashMap只对写加锁,针对读操作不加锁。
我们的读操作没有进行加锁,而是使用了volatile来保证我们每次都是从内存读取的结果,只针对写操作加锁,加锁仍然是使用"桶锁"(针对每个链表加锁,降低了锁冲突的概率)。
3.ConcurrentHashMap内部充分利用CAS特性,来减少加锁的操作,比如通过CAS来维护size属性
4.针对扩容操作,采取了"化整为零"的策略
我们的HashMap和Hashtable扩容这一操作,是直接创建一个新数组,然后将旧的数组上的每个元素搬到新数组上,但是如果我们哈希表中的元素特别多时,当我们某一次进行put时,会突然感到十分的耗时。
我们ConcurrentHashMap这里采取的策略是: 创建一个新的数组,每次只搬运一小部分,当进行put操作时,直接往新数组上添加,同时搬运一部分旧的元素到新数组上,当进行get操作时,新旧数组都进行查询,当进行remove操作时,新旧数组先查询,查询到了直接进行删除即可,当我们所有元素都搬运好了之后,然后在释放旧数组。

** Hashtable和HashMap、ConcurrentHashMap 之间的区别?**
HashMap: 线程不安全,key允许为Null
Hashtable: 线程安全,并且key不允许为Null,每个方法使用Synchronized加锁
ConcurrentHashMap: 线程安全,并且key不允许为Null,使用Synchronized锁每个链表的头结点元素,降低锁冲突概率,充分利用了CAS机制,优化了扩容机制,采用"化整为零"的策略。

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

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

相关文章

2023我的创作纪念日

文章目录机缘收获日常憧憬机缘 这个博客还是我上大一的时候注册的,在大一、大二、大三期间更多的是为了方便搜索,学校里边的习题大部分是可以在CSDN上找到的。真正写博客是在大三下学习实习,当时为了方便记录实习中遇到的问题。在C站对我影响…

【Git 从入门到精通】2023最新版的Git安装与卸载每一步附详细讲解

文章目录安装1.下载Git2.开始安装卸载1.找到电脑中的Git2.卸载3.删除环境变量安装 1.下载Git 首先去官网下载Git安装包,可以直接在百度搜索Git,以下几个网站都可以。也可以点击直达,官网上下载如果不科学上网的话还是很慢的,所以…

[Zombodb那些事]Zombodb与ElasticSearch的Bulk通信

Zombodb与ElasticSearch的Bulk通信0.前言Zombodb是一个PostgreSQL插件,使用rust编写,支持pg14以下版本。Zombodb可以允许PostgreSQL查询ElasticSearch中的内容。本篇为《Zombodb那些事》第一篇,后面将更新其他部分内容。Zombodb会在pg数据库上…

智能文字识别技术推动彝文识别弘扬中华文化

前言 谈起图像识别自己颇有感触,因为之前的两段工作经历都和图像识别密切相关;之前一家公司的主营业务就是将历史上珍贵文献进行数字化;上家公司自己负责图像识别模块相关的工作;不但使用了第三方平台产品而且进行了自建&#xff…

设计模式相关内容介绍—UML

统一建模语言(Unified ModelingLanguage,UML)是用来设计软件的可视化建模语言。它的特点是简单、统一、图形化、能表达软件设计中的动态与静态信息。 UML从目标系统的不同角度出发,定义了用例图、类图、对象图、状态图、活动图、时序图、协作图、构件图、…

经过2022年这大环境,我学会了如何管理我的领导

2022年这大环境,可以说是我干软件开发这些年来,经历的最残酷的一年,所以做为职场软件开发一员的我,不得不修炼一下真本事。 很多时候不是你不努力,不是你连mysql连的不溜,不是你布局页面布局的不精细&#…

16.Isaac教程--Codelets详解

Codelets详解 ISAAC教程合集地址: https://blog.csdn.net/kunhe0512/category_12163211.html 文章目录Codelets详解Codelets 和 tick接收消息传输消息方便的 ToProto/FromProto 函数配置参数应用程序 JSON子图姿态组件是机器人应用程序的基本构建块。 Isaac SDK 包含可在您的应…

「数据结构详解·九」图的初步

「数据结构详解一」树的初步「数据结构详解二」二叉树的初步「数据结构详解三」栈「数据结构详解四」队列「数据结构详解五」链表「数据结构详解六」哈希表「数据结构详解七」并查集的初步「数据结构详解八」带权并查集 & 扩展域并查集「数据结构详解九」图的初步 注意&…

基于JavaWEB SSM SpringBoot婚纱影楼摄影预约网站设计和实现

基于JavaWEB SSM SpringBoot婚纱影楼摄影预约网站设计和实现 博主介绍:5年java开发经验,专注Java开发、定制、远程、文档编写指导等,csdn特邀作者、专注于Java技术领域 作者主页 超级帅帅吴 Java毕设项目精品实战案例《500套》 欢迎点赞 收藏 ⭐留言 文末…

天宝营养冲刺深交所IPO:业绩明显波动,深创投是股东

撰稿|汤汤 来源|贝多财经 近日,贝多财经发现,天宝动物营养科技股份有限公司(下称“天宝营养”)递交预披露更新招股书,准备在深圳证券交易所主板上市,红塔证券为其独家保荐人。本次冲刺上市,天…

《Protein Actions Principles and Modeling》-《蛋白质作用原理和建模》中文分享(16)

​《Protein Actions Principles and Modeling》-《蛋白质作用原理和建模》 本人能力有限,如果错误欢迎批评指正。 第四章:Protein Binding Leads to Biological Actions (蛋白质的结合会产生生物作用) -在变构中,…

大神推荐,这几个电脑实用技巧,让你电脑用起来更加流畅舒服

电脑在我们的日常生活中,往往承担着“办公学习”的作用!所以我们应该掌握哪些常用、好用的电脑使用技巧呢?今天就给大家分享下,我日常在使用电脑过程中,经常会使用到的几个电脑使用技巧!第一:快…

基于FPGA的UDP 通信(三)

目录 引言 设计框图 UDP接收模块 设计源码 TEST BENCH 仿真结果 引言 前文链接: 基于FPGA的UDP 通信(一) 基于FPGA的UDP 通信(二) 本文基于FPGA设计千兆以太网通信模块:FPGA接收上位机数据。后续…

端到端的传输协议

(一)如何在一条物理链路上进行有效和可靠的数据传输 ——数据链路层传输协议 (1)标识高层送下来的数据块的起止、特定内容(例如校验比特)的位置 ——组帧技术 (2)如何发现传输中的错…

数据结构---线性表

刘佳瑜*,王越 *, 黄扬* , 张钊* (淮北师范大学计算机科学与技术学院,安徽 淮北) *These authors contributed to the work equllly and should be regarded as co-first authors. 🌞欢迎来到数据结构的世界 🌈博客主页&#xff1…

回溯法--最大团问题

问题描述什么是最大团?最大团的定义?完全图:如果无向图中的任何一对顶点之间都有一条边,这种无向图称为完全图。完全子图:给定无向图G(V,E)。如果U⊆V,且对任意u,v⊆U 有(u,v) ⊆ E&…

ZigBee 3.0实战教程-Silicon Labs EFR32+EmberZnet-5-02:串口发送数据-hello world

【源码、文档、软件、硬件、技术交流、技术支持,入口见文末】 【所有相关IDE、SDK和例程源码均可从群文件免费获取,免安装,解压即用】 持续更新中,欢迎关注! 前面《ZigBee 3.0实战教程-Silicon Labs EFR32EmberZnet-5…

90 后学霸博士 8 年进击战:用机器学习为化工研究叠 BUFF

本文首发自微信公众号:HyperAI超神经 内容一览:ScienceAI 作为近两年的技术热点,引起了业界广泛关注和讨论。本文将围绕 ScienceAdvances 的一篇论文,介绍如何利用机器学习,对燃煤电厂的胺排放量进行预测。 关键词&…

初始化一个GCP项目并用gcloud访问操作

1 简介 谷歌云GCP(Google Cloud Platform)是由Google提供的云平台,还是为用户提供了许多免费的产品,还是可以尝试一下的。对于学习或者小项目,都可以使用。 2 创建一个新项目 要使用GCP,我们需要创建一个…

【日常业务开发】常用JSON库API

【日常业务开发】常用JSON库APIGsonJava 对象转 Json字符串(序列化)Json字符串转Java 对象(反序列化)FastJsonJava 对象转 Json字符串(序列化)Json字符串转Java 对象(反序列化)JacksonJava 对象转 Json字符串(序列化)Json字符串转Java 对象(反序列化)Json 字符串内容反序列化为…