并发编程——synchronized

文章目录

  • 原子性、有序性、可见性
    • 原子性
    • 有序性
    • 可见性
  • synchronized使用
  • synchronized锁升级
  • synchronized-ObjectMonitor

原子性、有序性、可见性

原子性

数据库事务的原子性:是一个最小的执行的单位,一次事务的多次操作要么都成功,要么都失败。

并发编程的原子性:一个或多个指令在CPU执行过程中不允许中断。

i++;操作是原子性?

肯定不是:i++操作一共有三个指令

image.png

getfield:从主内存拉取数据到CPU寄存器

iadd:在寄存器内部对数据进行+1

putfield:将CPU寄存器中的结果更新搭配主内存中

如何保证i++是原子性?

使用synchronized、lock、Atomic(CAS)来保证

image.png

使用lock锁也会有类似的概念,也就是在操作i++的三个指令前,先基于AQS成功修改state后才可以操作

使用synchronized和lock锁时,可能会触发将线程挂起的操作,而这种操作会触发内核态和用户态的切换,从而导致消耗资源。

CAS方式就相对synchronized和lock锁的效率更高,因为CAS不会触发线程挂起操作!

CAS:compare and swap

线程基于CAS修改数据的方式:先获取主内存数据,在修改之前,先比较数据是否一致,如果一致修改主内存数据,如果不一致,放弃这次修改

CAS就是比较和交换,而比较和交换是一个原子操作

image.png

CAS在Java层面就是Unsafe类中提供的一个native方法,这个方法只提供了CAS成功返回true,失败返回false,如果需要重试策略需要自己实现

CAS问题:

  • CAS只能对一个变量的修改实现原子性。
  • CAS存在ABA问题。
    • A线程修改主内存数据从1~2,卡在了获取1之后。
    • B线程修改主内存数据从1~2,完成。
    • C线程修改主内存数据从2~1,完成。
    • A线程执行CAS操作,发现主内存是1,没问题,直接修改
    • 解决方案:加版本号
  • 在CAS执行次数过多,但是依旧无法实现对数据的修改,CPU会一直调度这个线程,造成对CPU的性能损耗
    • synchronized的实现方式:CAS自旋一定次数后,如果还不成,挂起线程
    • LongAdder的实现方式:当CAS失败后,将操作的值,存储起来,后续一起添加

有序性

指令在CPU调度执行时,CPU会为了提升执行效率,在不影响结果的前提下,对CPU指令进行重新排序。但是这样可能会造成数据的不一致。

如果不希望CPU对指定进行重排序,怎么办?

可以对属性追加volatile修饰,就不会对当前属性的操作进行指令重排序。

可见性

CPU在处理时,需要将主内存数据拿到寄存机中再执行指令,执行完指令后,需要将寄存器数据扔回到主内存中。但是寄存器数据同步到主内存是遵循MESI协议的,简单来说就是:不是每次操作结束就将CPU缓存数据同步到主内存,这样就会造成多个线程看到的数据不一样。

所以通常需要synchronized和volatile配合解决这一问题:

  • volatile每次操作后,立即同步数据到主内存。
  • synchronized,只有一个线程操作这个数据。

synchronized使用

使用方法:声明方法时使用synchronized或者在代码块中使用synchronized。
锁类型:

  • 类锁:基于当前类的Class加锁
  • 对象锁:基于this对象加锁

synchronized是互斥锁,每个线程获取synchronized时,基于synchronized绑定的对象去获取锁!

synchronized是如何基于对象实现的互斥锁,先了解对象再内存中是如何存储的。

image.png

在Java中查看对象的存储:

导入依赖:

<dependency><groupId>org.openjdk.jol</groupId><artifactId>jol-core</artifactId><version>0.9</version>
</dependency>

查看对象信息

image.png

synchronized锁升级

synchronized在jdk1.6之前,一直是重量级锁:只要线程获取锁资源失败,直接挂起线程。

jdk1.6之前synchronized效率贼低,再加上Doug Lea推出了ReentrantLock,效率比synchronized快多了,导致JDK团队不得不在jdk1.6将synchronized做优化。

锁升级:

  • 无锁状态、匿名偏向状态:没有线程拿锁。
  • 偏向锁状态:没有线程的竞争,只有一个线程在获取锁资源。
    线程竞争锁资源时,发现当前synchronized没有线程占用锁资源,并且锁是偏向锁,使用CAS的方式,设置线程ID为当前线程,获取到锁资源,下次当前线程再次获取时,只需要判断是偏向锁,并且线程ID是当前线程ID即可,直接获得到锁资源。
  • 轻量级锁:偏向锁出现竞争时,会升级到轻量级锁。
    轻量级锁的状态下,线程会基于CAS的方式,尝试获取锁资源,CAS的次数是基于自适应自旋锁实现的,JVM会自动的基于上一次获取锁是否成功,来决定这次获取锁资源要CAS多少次。
  • 重量级锁:轻量级锁CAS一段次数后,没有拿到锁资源,升级为重量级锁(其实CAS操作是在重量级锁时执行的)。重量级锁就是线程拿不到锁,就挂起。

偏向锁是延迟开启的,并且在开启偏向锁之后,默认不存在无锁状态,只存在匿名偏向synchronized因为不存在从重量级锁降级到偏向或者是轻量。

synchronized在偏向锁升级到轻量锁时,会涉及到偏向锁撤销,需要等到一个安全点,stw,才可以撤销,并发偏向锁撤销比较消耗资源。在程序启动时,偏向锁有一个延迟开启的操作,因为项目启动时,ClassLoader会加载.class文件,这里会涉及到synchronized操作。为了避免启动时涉及到偏向锁撤销,导致启动效率变慢,所以程序启动时,默认不是开启偏向锁的。

编译器优化的结果,出现了下列效果

  • 锁消除:线程在执行一段synchronized代码块时,发现没有共享数据的操作,自动帮你把synchronized去掉。

  • 锁粗化:在一个多次循环的操作中频繁的获取和释放锁资源,synchronized在编译时,可能会优化到循环外部。

synchronized-ObjectMonitor

ObjectMonitor一般是到达了重量级锁才会涉及到。在到达重量级锁之后,重量级锁的指针会指向ObjectMonitor对象。

  ObjectMonitor() {_header       = NULL;_count        = 0;     // 抢占锁资源的线程个数_waiters      = 0,     // 调用wait的线程个数。_recursions   = 0;     // 可重入锁标记,_object       = NULL; _owner        = NULL;  // 持有锁的线程_WaitSet      = NULL;  // wait的线程  (双向链表)_WaitSetLock  = 0 ;_Responsible  = NULL ;_succ         = NULL ;  // 假定的继承人(锁释放后,被唤醒的线程,有可能拿到锁资源)_cxq          = NULL ;  // 挂起线程存放的位置。(单向链表)FreeNext      = NULL ;_EntryList    = NULL ;  // _cxq会在一定的机制下,将_cxq里的等待线程扔到当前_EntryList里。  (双向链表)_SpinFreq     = 0 ;_SpinClock    = 0 ;OwnerIsThread = 0 ;_previous_owner_tid = 0;}

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

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

相关文章

mtk安卓启动动画与drm简解

mtk-drm初始化 mtk-drm commit送显流程&#xff08;一&#xff09; 背景 在使用安卓双屏时&#xff0c;发现两个屏幕不是同时点亮的&#xff0c;两个屏幕不是直连到soc的显示接口&#xff0c;是通过ti方案941-948这种方案连接的&#xff0c;需要配置才能显示到屏幕&#xff0c…

蓝桥杯 题库 简单 每日十题 day6

01 删除字符 题目描述 给定一个单词&#xff0c;请问在单词中删除t个字母后&#xff0c;能得到的字典序最小的单词是什么&#xff1f; 输入描述 输入的第一行包含一个单词&#xff0c;由大写英文字母组成。 第二行包含一个正整数t。 其中&#xff0c;单词长度不超过100&#x…

记录selenium和chrome使用socks代理打开网页以及查看selenium的版本

使用前&#xff0c;首先打开socks5全局代理。 之前我还写过一篇关于编程中使用到代理的情况&#xff1a; 记录一下python编程中需要使用代理的解决方法_python 使用全局代理_小小爬虾的博客-CSDN博客 在本文中&#xff0c;首先安装selenium和安装chrome浏览器。 参考我的文章…

【Spring IOC容器加载过程】

Spring IOC容器加载分为以下几个步骤&#xff1a; 定位配置文件&#xff1a;首先需要定位Spring配置文件&#xff0c;Spring提供了多种方式&#xff0c;如通过ClassPath、FileSystem等方式进行定位。 加载配置文件&#xff1a;在定位到配置文件后&#xff0c;通过BeanDefiniti…

用VS Code运行C语言(安装VS Code,mingw的下载和安装)

下载并安装VS code。 安装扩展包&#xff1a; 此时&#xff0c;写完代码右键之后并没有运行代码的选项&#xff0c;如图&#xff1a; 接下来安装编译器mingw。 下载链接&#xff1a; https://sourceforge.net/projects/mingw-w64/ 得到压缩包&#xff1a; 解压&#xff1a; …

外呼系统和呼叫中心系统的优势和特点

在金融投资、教育培训、保险、互联网、旅游、房地产、广告等行业服务或产品电话营销中&#xff1b;在节日促销、招商加盟、活动通知等项目中&#xff1b;作为企业的管理者的您&#xff0c;是否还在因为销售人员效率低&#xff0c;人员成本高等问题头疼? 其实&#xff0c;您只…

滚雪球学Java(26):Java进制转换

&#x1f3c6;本文收录于「滚雪球学Java」专栏&#xff0c;专业攻坚指数级提升&#xff0c;助你一臂之力&#xff0c;带你早日登顶&#x1f680;&#xff0c;欢迎大家关注&&收藏&#xff01;持续更新中&#xff0c;up&#xff01;up&#xff01;up&#xff01;&#xf…

19异常的学习笔记

异常 很重要&#xff0c;有利于我们平时处理问题 异常就是代表程序出现了问题 常见的异常比如说 数组越界除法除0 异常的体系是什么 java.lang.Throwable Error Exception RuntimeException 其他异常 Error 代表的是系统级别的错误&#xff0c;也就是一旦系统出现问题&…

由于数字化转型对集成和扩展性的要求,定制化需求难以满足,百数低代码服务商该如何破局?

当政策、技术环境的日益成熟&#xff0c;数字化转型逐步成为企业发展的必选项&#xff0c;企业数字化转型不再是一道选择题&#xff0c;而是决定其生存发展的必由之路。通过数字化转型升级生产方式、管理模式和组织形式&#xff0c;激发内生动力&#xff0c;成为企业顺应时代变…

最新适合小白前端 Javascript 高级常见知识点详细教程(每周更新中)

1. window.onload 窗口或者页面的加载事件&#xff0c;当文档内容完全加载完成会触发的事件&#xff08;包括图形&#xff0c;JS脚本&#xff0c;CSS文件&#xff09;&#xff0c;就会调用处理的函数。 <button>点击</button> <script> btn document.q…

python项目2to3方案预研

目录 官方工具2to3工具安装参数解释基本使用工具缺陷 future工具安装参数解释基本使用工具缺陷 python-modernize工具安装参数解释基本使用工具缺陷 pyupgrade工具安装参数解释基本使用工具缺陷 对比 官方工具2to3 2to3 是Python官方提供的用于将Python 2代码转换为Python 3代…

如何在 CentOS 8 上安装 OpenCV?

OpenCV( 开源计算机视觉库)是一个开放源代码计算机视觉库&#xff0c;支持所有主要操作系统。它可以利用多核处理的优势&#xff0c;并具有 GPU 加速功能以实现实时操作。 OpenCV 的用途非常广泛&#xff0c;包括医学图像分析&#xff0c;拼接街景图像&#xff0c;监视视频&am…

单例模式(饿汉模式 懒汉模式)与一些特殊类设计

文章目录 一、不能被拷贝的类 二、只能在堆上创建类对象 三、只能在栈上创建类对象 四、不能被继承的类 五、单例模式 5、1 什么是单例模式 5、2 什么是设计模式 5、3 单例模式的实现 5、3、1 饿汉模式 5、3、1 懒汉模式 &#x1f64b;‍♂️ 作者&#xff1a;Ggggggtm &#x…

VM虚拟机CentOS7.9x64 LVM硬盘扩容

软件版本&#xff1a;VMWare Workstation14 虚拟机CentOS 7.9X64位 GParted 0.33.0 一、虚拟机安装gparted软件 sudo yum install epel-release sudo yum install gparted sudo yum install yum-utils git gnome-common gcc-c sudo yum-builddep gparted 二、关闭虚拟机&a…

一、Java简介

基础知识 1.1.Java简介1.1.1.什么是Java语言1.1.2.Java应用的领域1.1.3.Java版本1.1.4.如何学好Java1.1.5.Java API文档1.1.6.Java语言特性 —————————————————————————————————————————————————— —————————————…

MySQL学习系列(1)-每天学习10个知识

目录 1. SQL 和 MySQL2. JOIN 类型3. 索引4. ACID5. 优化MySQL查询性能6. 创建MySQL存储过程的步骤7. 范式和反范式设计8. SQL注入9. GROUP BY 和 HAVING10. UNION 和 UNION ALL &#x1f44d; 点赞&#xff0c;你的认可是我创作的动力&#xff01; ⭐️ 收藏&#xff0c;你的…

java 性能分析:如何提高 Java 程序的性能

示例示例Java性能分析是指通过对Java程序进行分析&#xff0c;以改善应用程序的性能。它可以帮助开发人员确定程序中的性能瓶颈&#xff0c;并对其进行优化。Java性能分析可以通过多种方式完成&#xff0c;比如使用JVM内置的性能分析工具&#xff0c;如jstat&#xff0c;jmap&a…

ffplay源码解析-main入口函数

main入口函数 初始化 变量、缓存区、SDL窗口初始化等 int main(int argc, char **argv) {int flags;VideoState *is; // av_log_set_level(AV_LOG_TRACE);init_dynload();av_log_set_flags(AV_LOG_SKIP_REPEATED);parse_loglevel(argc, argv, options);/// av_log_set_le…

Hive行转列[一行拆分成多行/一列拆分成多列]

场景&#xff1a; hive有张表armmttxn_tmp&#xff0c;其中有一个字段lot_number&#xff0c;该字段以逗号分隔开多个值&#xff0c;每个值又以冒号来分割料号和数量&#xff0c;如&#xff1a;A3220089:-40,A3220090:-40,A3220091:-40,A3220083:-40,A3220087:-40,A3220086:-4…

深入理解JVM虚拟机第九篇:详细介绍JVM中的双亲委派机制工作原理和机制

文章目录 一:双亲委派机制 1:工作原理 2:代码展示 3:异常代码展示