并发编程——synchronized

2023-09-20 21:18:05

原子性、有序性、可见性

原子性

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

并发编程的原子性:一个或多个指令在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;
  }
更多推荐

CentOS7安装MySQL

文章目录前言一、MySQL5.71.1安装wget1.2下载&安装MySQL的rpm源1.3修改MySQL安装版本1.4下载并启动MySQL1.5开启MySQL远程连接用户二、MySQL8.0注意事项前言CentOS7的安装,采用的是yum的方式安装。yum方式安装,就类似在Windows下不停的下一步,下一步。根据官

灰度变换原理//test later

灰度变换原理图像灰度变换变换原理:通过变换函数T将原图像像素灰度值r映射为灰度值s:2、灰度反转2.1原理灰度反转:将图像亮暗对调,可以增强图像中暗色区域细节�=�(�)=�−1−�其中L为图像灰度级,0~255灰度图像的灰度级为256.2.2c++opencv代码#include<iostream>#include<

如何快速检测代理IP质量?

一直以来,IP代理都是出海跨境业务的刚需。质量好的IP代理,除了在跨境业务产生巨大作用,在SEO监控、爬虫抓取、市场研究等领域也发挥着很大的作用。但是,对于IP代理的质量检测是我们选择高标准IP代理的一句,我们一般都会建议在使用IP代理前,进行测试或检测。那么我们应该如何快速地检测代理IP的有效性与质量的好坏呢?接下来

第三章 关系数据库标准语言SQL

第三章关系数据库标准语言SQL3.1SQL概述3.1.1SQL的产生与发展3.1.2SQL的特点主要特点:综合统一集数据定义语言(DDL)、数据操纵语言(DML)、数据控制语言(DCL)的功能于一体}数据操作统一可以独立完成数据库生命周期中的全部活动,包括以下一系列操作要求:定义和修改、删除关系模式,定义和删除视图,插

Java版分布式微服务云开发架构 Spring Cloud+Spring Boot+Mybatis 电子招标采购系统功能清单

项目说明随着公司的快速发展,企业人员和经营规模不断壮大,公司对内部招采管理的提升提出了更高的要求。在企业里建立一个公平、公开、公正的采购环境,最大限度控制采购成本至关重要。符合国家电子招投标法律法规及相关规范,以及审计监督要求;通过电子化平台提高招投标工作的公开性和透明性;通过电子化招投标,使得招标采购的质量更高、速度

日志输出-查看 SQL:深入分析 MyBatis 执行过程

😀前言在现代软件开发中,数据库操作是不可或缺的一部分,而持久层框架的应用能够极大地简化这一过程。然而,当我们在开发MyBatis程序时,有时候需要深入了解程序底层实际执行的SQL语句,以便更好地分析和优化数据库操作。本文将探讨如何通过配置日志输出,在MyBatis中查看SQL语句的执行情况,以便更深入地了解其执行过程

逻辑回归中对L1\L2正则化的理解

在逻辑回归中,L1和L2正则化是常用的正则化技术,用于控制模型的复杂度并防止过拟合。它们通过在损失函数中引入额外的正则化项来实现。L1正则化(Lasso正则化):L1正则化使用参数权重的绝对值之和作为正则化项。其目标是将一些权重压缩为零,从而实现特征选择的效果。L1正则化的数学形式如下:R(w)=λ∑i=1n∣wi∣\

英语CN专刊《英语教师》简介及投稿须知

英语CN专刊《英语教师》简介及投稿须知《英语教师》杂志是由中华人民共和国新闻出版总署、正式批准公开发行的优秀期刊,《英语教师》系一本面向基础教育和高等教育英语教师的、兼顾理论性与实践性的专业性期刊。《英语教师》的读者对象主要是广大英语教师、英语教研员以及高校外语院系学生。本刊主要刊载有关英语教学和英语教师教育的论文、实

Linux 系统移植(二)--系统调试

文章目录一、编译文件系统1.1下载资源安装包1.2配置模板ARM64目标平台1.3配置交叉编译器1.4配置登录用户名和密码1.5配置Linux控制台1.6配置文件系统格式1.7编译buildroot文件系统二、编译ARM64Linux三、启动QemuLinux系统参考链接:一、编译文件系统1.1下载资源安装包我们使用b

(日积月累版)大数据基础知识点1-关系型数据库

好久不见,甚是想念。笔者最近有时间整理关于大数据的一些基础知识点,整理的目不在于能提升多少技能,关键在于巩固一些很基础的知识点,毕竟互联网就是基础略稳固的人比较有优势,在遇到或发现一些技术问题时,从底层科学的去理解这些问题,说不定会有另一片天下。那么本期带来大数据面是:关系型数据库一、什么是关系型数据库?关系型数据库是

全国职业技能大赛云计算--高职组赛题卷④(私有云)

全国职业技能大赛云计算--高职组赛题卷④(私有云)第一场次题目:OpenStack平台部署与运维任务1基础运维任务(5分)任务3OpenStack云平台运维(15分)任务4OpenStack云平台运维开发(15分,本任务只公布考试范围,不公布赛题)需要环境私信博主!!!第一场次题目:OpenStack平台部署与运维某企

热文推荐