synchronzied

news2024/9/27 21:28:13

synchronzied的作用

  • 原子性:所谓原子性就是一个操作或者多个操作,要么全部执行并且执行的过程不会被任何因素打断,要么都不执行。被synchronzied修饰的类或对象的所有操作都是原子的,因为在执行之前必须先获得类或对象的锁、直到执行完才能释放。
  • 可见性:可见性是指多个线程访问一个资源时,该资源的状态、值信息等对于其他线程都是可见的。synchronized具有可见性,其中synchronized对一个类或对象加锁时,一个线程如果要访问该类或对象必须先获得它的锁,而这个锁的状态对于其他任何线程都是可见的,并且在释放锁之前会将对变量的修改刷新到共享内存当中,保证资源变量的可见性。
  • 有序性:有序性指程序执行的顺序按照代码先后执行。synchronized具有有序性,Java允许编译器和处理器对指令进程重排,但是指令重排并不会影响单线程的顺序,它影响的是多线程并发执行的顺序性。synchronized保证了每个时刻都只有一个线程访问同步代码块,也就确定了线程执行同步代码块是分先后顺序的,保证了有序性。

synchronzied的主要用法

  • 修饰实例方法:作用于当前对象实例加锁,进入同步代码前要获得当前对象实例的锁(对于普通同步方法,锁的是当前实例对象,通常指this)
synchronized void method(){

}
  • 修饰静态方法(对于静态同步方法,锁的是当前类的Class对象)
synchronzied void static method(){

}
  • 修饰代码块(指定加锁对象,对给定对象/类加锁。synchronized(this|object)表示进入同步代码库前要获得给定对象的锁。synchronized(类.class)表示进入同步代码前要获得当前class的锁)
synchronized(this) {
  //业务代码
}

对于普通同步方法,锁是当前实例对象;对于静态同步方法,锁是当前类的Class对象;对于同步方法块,锁是synchronized括号里配置的对象。

当一个线程试图访问同步代码块时,它首先必须得到锁,退出或抛出异常时必须释放锁。那么锁到底在哪里呢?锁里面会存储什么信息呢?

从JVM规范中可以看到synchronized在JVM里的实现原理,JVM基于进入和退出Monitor对象来实现方法同步和代码块同步,但两者的实现细节不一样。代码块同步是使用monitorenter和monitorexit指令实现的。

monitorenter指令是在编译后插入到同步代码块的开始位置,而monitorexit是插入到方法结束处和异常处,JVM要保证每个monitorenter必须有对应的monitorexit与之配对。任何对象都有一个monitor与之关联,当且一个monitor被持有后,它将处于锁定状态。线程执行到monitorenter指令时,将会尝试获取对象所对应的monitor的所有权,即尝试获得对象的锁。

synchronized用的锁是存在Java对象头里的。
Java对象头里的Mark Word里默认存储对象的HashCode、分段年龄和锁标记位
在这里插入图片描述

synchronized为可重入锁的原理

每个锁对象拥有一个锁计数器一个指针,该指针是用来指向获得锁的线程的
当执行monitorenter时,如果目标锁对象的计数器为零,那么说明它没有被其他线程所持有,Java虚拟机会将该锁对象的持有线程设置为当前线程,并且将其计数器加1.
在目标锁对象的计数器不为零的情况下,如果锁对象的持有线程是当前线程,那么Java虚拟机可以将其计数器加1,否则需要等待,直至持有线程释放锁。
当执monitorexit时,Java虚拟机则需要将锁对象的计数器减1。计数器为零代表锁已被释放。

内核态和用户态

CPU有两种工作状态:内核态和用户态。系统中既有操作系统的程序,也有普通用户的程序。为了安全和稳定性操作系统的程序不能随便访问,这就是内核态。内核态可以使用所有的硬件资源。用户态不能直接使用系统资源,也不能改变CPU的工作状态,并且只能访问这个用户程序自己的存储空间。

synchronized的背景

Java的线程是映射到操作系统原生线程之上的,如果要阻塞或唤醒一个线程就需要操作系统介入,需要在用户态与核心态之间切换,这种切换会消耗大量的系统资源,因为用户态与内核态都有各自的内存空间,专用的寄存器等,用户态切换至内核态需要传递给许多变量、参数给内核,内核也需要保护好用户态在切换时的一些寄存器值、变量等,以便内核态调用结束后切换回用户态继续工作。
在Java早期版本中,synchronized属于重量级锁,效率低下,因为监视器锁(monitor)是依赖于底层的操作系统的Mutex Lock(系统互斥量)来实现的,挂起线程和恢复线程都需要转入内核态去完成,阻塞或唤醒一个Java线程需要操作系统切换CPU状态来来完成,这种状态切换需要耗费处理器时间,如果同步代码块中内容过于简单,这种切换的时间可能比用户代码执行的时间还长,时间成本相对较高,这也是为什么早期的synchronized效率低的原因,Java6之后,为了减少获得锁和释放锁所带来的性能消耗,引入了轻量级锁和偏向锁。

synchronized锁升级

Java SE1.6中,锁一共有4种状态,级别从低到高依次是:无锁状态、偏向锁状态、轻量级锁状态和重量级锁状态,这几个状态会随着竞争情况逐渐升级。锁可以升级但不能降级

无锁
初始状态,一个对象被实例化后,如果还没有被任何线程竞争锁,那么它就是无锁状态。

偏向锁
(单线程竞争)
当线程A第一次竞争到锁时,通过操作修改Mark Word中的偏向线程ID为偏向模式
Hotspot的作者经过研究发现,大多数情况下:多线程的情况下,锁不仅不存在多线程竞争,还存在锁由同一个线程多次获得的情况,偏向锁就是在这种情况下出现的,它的出现是为了解决只有一个线程执行同步时提高性能。
在实际应用运行过程中发现,“锁总是同一个线程持有,很少发生竞争”,也就是说锁总是被第一个占用它的线程拥有,这个线程就是锁的偏向线程。
那么只需要在锁第一次被拥有的时候,记录下偏向线程ID。这样偏向线程就一直持有着锁(后续这个线程进入和退出这段加了同步锁的代码块时,不需要再次加锁和释放锁。而是直接会去检查锁的MarkWord里面是不是放的自己的线程ID)。
如果存放的是自己的线程ID,表示偏向锁是偏向于当前线程的,就不需要再尝试获得锁了。以后每次同步,检查锁的偏向线程ID与当前线程ID是否一致,如果一致直接进入同步。无需每次加锁解锁都去CAS更新对象头。如果自始至终使用锁的线程只有一个,很明显偏向锁几乎没有额外的开销,性能极高。
如果存放的不是自己的线程ID,就表示发生了竞争,锁已经不是总偏向于同一个线程了,这个时候会尝试使用CAS来替换MarkWord里面的线程ID为新线程的ID,如果竞争成功,表示之前的线程不存在了,MarkWord里面的线程ID为新线程的ID,锁不会升级,仍然为偏向锁,如果竞争失败,这时候可能需要升级为轻量级锁,才能保证线程间公平竞争锁。

轻量级锁
轻量级锁的加锁
JVM会为每个线程在当前线程的栈帧中创建用于存储锁记录的空间,官方称为Displaced Mark Word。若一个线程获得锁时发现是轻量级锁,会把锁的MarkWord复制到自己的Displaced Mark Word里面。然后线程尝试用CAS将锁的MarkWord替换为指向锁记录的指针。如果成功,当前线程获得锁,如果失败,表示Mark Word已经被替换成了其他线程的锁记录,说明在于其他线程竞争锁,当前线程就尝试使用自旋来获取锁。
轻量级锁的释放
在释放锁时,当前线程会使用CAS操作将Displaced Mark Word的内容复制回锁的Mark Word里面。如果没有发生竞争,那么这个复制的操作会成功。如果有其他线程因为自旋多次导致轻量级锁升级成了重量级锁,那么CAS操作会失败,此时会释放锁并唤醒被阻塞的线程。

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

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

相关文章

直播运营|如何打造可复制的直播增长闭环?

作为当下最热门的营销模式,直播带货对人员、场地及流程的把控等都提出了严格要求。而要提升直播运营、促成更高转化,直播复盘是关键的一环。 那么,直播后到底该如何高效复盘,为带货提效呢? 「帷幄开播 Whale Cast」新功…

_Linux 进程信号-基础篇

文章目录信号入门1. 生活角度2. Linux技术应用角度3. 知识小点4. 信号概念5. kill -l命令6. 信号处理常见方式产生信号1. 通过终端按键产生信号Core Dump2. 调用系统函数向进程发信号系统调用接口概述3. 由软件条件产生信号软件条件给进程发送信号概述4. 硬件异常产生信号理解除…

VS2012编译libjson库过程

下载libjson库 https://sourceforge.net/projects/libjson/ 最新版是2012-06-25的libjson_7.6.1.zip,大小为759.5 kB 解压缩包 由于是旧版本VS创建的项目,无法用新版VS直接打开项目编译了 使用VS2012新建一个Win32项目libjson 选择静态库,点完成 创建完成后项目列表 将l…

【Linux】-- 操作系统进程的状态

目录 描述进程-PCB 状态理论 Linux内核源代码的描述 R运行状态与S睡眠状态: 前台进程与后台进程 D磁盘休眠状态: T停止状态 X死亡状态 Z(zombie)-僵尸进程 僵尸进程的危害 进程状态总结 孤儿进程 进程优先级 Linux具体的优先级做法 PRI …

driftingblues6靶机(脏牛漏洞)

环境准备 靶机链接:百度网盘 请输入提取码 提取码:463a 虚拟机网络链接模式:桥接模式 攻击机系统:kali linux 2021.1 信息收集 1.arp-scan -l 探测目标靶机 2.nmap -p- -A -T4 192.168.1.107 探测目标靶机开放端口和服务 …

Internet Download Manager2023稳定版下载器

Internet Download Manager(简称IDM) 是Windows平台老牌而功能强大的下载工具,一种将下载速度提高多达5倍。那如果想要使用这款软件,那就需要安装这款软件,如何正确的下载和安装呢?今天,小编就教…

【ES】 es | Elasticsearch 教程 | DSL命令 | 命令操作es

一、说明 1、通过kibana操作es 2、使用dsl命令操作es 3、需要已经安装es,必须 4、需要已经安装kibana,非必须 5、若是没有装kibana,可以用PostMan之类的请求工具 二、基础知识 1、Elasticsearch和RDBMS比较 说明1: es与传统关系数…

树莓派串口通信

文章目录一、树莓派串口介绍二、串口通信配置1.打开USART串口2.串口映射配置3.安装mini串口调试助手4. 解决打开ttyAMA0 时没有权限5.解决打开minicom串口助手的时候,键盘失灵一、树莓派串口介绍 树莓派4B一共包含两个串口,一个是硬件串口(/…

LVGL学习笔记13 - 圆弧Arc

目录 1. Parts 2. 样式 2.1 旋转 2.3 设置角度范围 2.4 设置值范围 2.5 清除Knob显示 2.6 禁止点击调整值 2.7 修改圆环前景色 2.8 修改圆环背景色 2.9 修改圆弧末端形态 2.10 修改圆弧宽度 2.11 修改圆弧透明度 3. 事件 圆弧由背景和前景弧组成,通过lv_ar…

密码学_SHA-1

SHA-1(Secure Hash Algorithm 1,安全散列算法1)是一种密码散列函数,美国国家安全局设计,并由美国国家标准技术研究所(NIST)发布为联邦数据处理标准(FIPS)。SHA-1可以生成…

SpringCloud Netflix复习之Eureka

写作背景 SpringCloud Netflix作为SpringCloud第一代产品很经典,而且公司的老项目还在用SpringCloud Netflix的技术栈,有必要对SpringCloud Netflix的各种核心组件回归复习一下了。 本次复习的主角是微服务注册中心Eureka,本文的书写思路是五…

【大厂高频真题100题】《给表达式添加运算符》 真题练习第22题 持续更新~

给表达式添加运算符 给定一个仅包含数字 0-9 的字符串 num 和一个目标值整数 target ,在 num 的数字之间添加 二元 运算符(不是一元)+、- 或 * ,返回 所有 能够得到 target 的表达式。 注意,返回表达式中的操作数 不应该 包含前导零。 示例 1: 输入: num = "123&q…

innovus中的DanglingWire(悬垂线)的理解和处理

在innovus工具里边,用户经常会使用verifyConnectivity 来进行open ,绕线完整性等问题的查验。对于绕线结果,尤其是PG绕线结果,使用这个命令可以很好的帮助用户在power planning阶段查验PG的闭合连接的状态(在pg DB中使…

JavaScript 监听单个元素高宽变化MutationObserver

如果你只是想监听窗口大小变化 resize就能满足你 window.onresize () > {console.log("窗口变化"); };jqurey写法 $(window).resize(()>{console.log("窗口变化"); })但如果是要监听某一块元素 resize就不太适用了 例如 我们在界面上定义一个类名…

MATLAB-条形图、饼状图绘制

条形图 bar命令用于绘制二维的垂直条形图,用垂直的条形显示向量或者矩阵的值,可以显示矢量数据和矩阵数据。使用格式如下。bar( y) %为每一个y中元素画出条形 bar( x,y) %在指定的横坐标x上画出y,其中x为严格单增的…

49_IAP实验

目录 IAP介绍 一般的程序执行流程 IAP程序执行流程 实验源码: IAP介绍 STM32编程方式: 1.在线编程(ICP,In-Circuit Programming):通过JTAG/SWD协议或者系统加载程序(Bootloader)下载用户应用程序到微控制器中。 2.在程序中编程(IAP,In Application Programmi…

MySQL复制底层技术——LOGICAL_CLOCK并行复制、WRITESET并行复制

1. LOGICAL_CLOCK 并行复制 1.原理 从MySQL 5.7版本开始,支持LOGICAL_CLOCK级别的并行复制(基于MySQL 5.6的库级别的Group Commit并行复制的大幅改进),通过设置参数slave_parallel_type为LOGICAL_CLOCK来启用&#x…

蓝桥杯STM32G431RBT6学习——工程建立

蓝桥杯STM32G431RBT6学习——工程建立 前言 我始终认为一边学习一边记录是最好的方式,一方面便知识的梳理,另一方面便于二次复习使用。最后一次参加蓝桥杯,希望能整个不错的成绩。–2023/1/4 准备工具 1、keil5 2、STM32G系列芯片包 3、S…

PostgreSQL在openEuler上本地yum不能安装而网络yum却可以的原因分析

作者:IT圈黎俊杰 PostgreSQL是国外著名的开源数据库,openEuler是国内的开源操作系统品牌,当前PostgreSQL对openEuler是没有适配认证的,PostgreSQL官方更不存在提供可直接在openEuler操作系统上安装的RPM软件包了。 在PostgreSQL官…

56、mysql的作业

作业中要用到的两张表dept,emp,找不到原来的sql语句了,所以只有表的内容 1、选择题 (1)以下哪条语句是错误的? A. SELECT empno, ename name, sal salary FROM emp; B. SELECT empno, ename name, sal AS s…