Synchronized 原理

news2024/11/14 19:38:21

基本特点(只考虑 JDK 1.8):

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

加锁工作过程

JVM 将 synchronized 锁分为 无锁、偏向锁、轻量级锁、重量级锁 状态。会根据情况,进行依次升级。



 

偏向锁

第一个尝试加锁的线程, 优先进入偏向锁状态
偏向锁不是真的 "加锁", 只是给对象头中做一个 "偏向锁的标记", 记录这个锁属于哪个线程.
如果后续没有其他线程来竞争该锁, 那么就不用进行其他同步操作了(避免了加锁解锁的开销)
如果后续有其他线程来竞争该锁(刚才已经在锁对象中记录了当前锁属于哪个线程了, 很容易识别
当前申请锁的线程是不是之前记录的线程), 那就取消原来的偏向锁状态, 进入一般的轻量级锁状态.
偏向锁本质上相当于 "延迟加锁" . 能不加锁就不加锁, 尽量来避免不必要的加锁开销.
但是该做的标记还是得做的, 否则无法区分何时需要真正加锁.

轻量级锁

随着其他线程进入竞争, 偏向锁状态被消除, 进入轻量级锁状态(自适应的自旋锁).
此处的轻量级锁就是通过 CAS 来实现.

  • 通过 CAS 检查并更新一块内存 (比如 null => 该线程引用)
  • 如果更新成功, 则认为加锁成功
  • 如果更新失败, 则认为锁被占用, 继续自旋式的等待(并不放弃 CPU).
     

自旋操作是一直让 CPU 空转, 比较浪费 CPU 资源.
因此此处的自旋不会一直持续进行, 而是达到一定的时间/重试次数, 就不再自旋了.
也就是所谓的 "自适应
 

重量级锁

如果竞争进一步激烈, 自旋不能快速获取到锁状态, 就会膨胀为重量级锁
此处的重量级锁就是指用到内核提供的 mutex .

  • 执行加锁操作, 先进入内核态.
  • 在内核态判定当前锁是否已经被占用
  • 如果该锁没有占用, 则加锁成功, 并切换回用户态.
  • 如果该锁被占用, 则加锁失败. 此时线程进入锁的等待队列, 挂起. 等待被操作系统唤醒.
  • 经历了一系列的沧海桑田, 这个锁被其他线程释放了, 操作系统也想起了这个挂起的线程, 于是唤醒这个线程, 尝试重新获取锁.

其他的优化操作

锁消除


编译器+JVM 判断锁是否可消除. 如果可以, 就直接消除

什么是 "锁消除"
有些应用程序的代码中, 用到了 synchronized, 但其实没有在多线程环境下. (例如 StringBuffer)
 

StringBuffer sb = new StringBuffer();
        sb.append("a");
        sb.append("b");
        sb.append("c");
        sb.append("d");

此时每个 append 的调用都会涉及加锁和解锁. 但如果只是在单线程中执行这个代码, 那么这些加
锁解锁操作是没有必要的, 白白浪费了一些资源开销.
 

锁粗化

一段逻辑中如果出现多次加锁解锁, 编译器 + JVM 会自动进行锁的粗化.

 

实际开发过程中, 使用细粒度锁, 是期望释放锁的时候其他线程能使用锁.
但是实际上可能并没有其他线程来抢占这个锁. 这种情况 JVM 就会自动把锁粗化, 避免频繁申请释
放锁

可以看到, synchronized 的策略是比价复杂的, 在背后做了很多事情, 目的为了让程序猿哪怕啥都不懂,也不至于写出特别慢的程序
 

什么是偏向锁?

偏向锁不是真的加锁, 而只是在锁的对象头中记录一个标记(记录该锁所属的线程). 如果没有其他线
程参与竞争锁, 那么就不会真正执行加锁操作, 从而降低程序开销. 一旦真的涉及到其他的线程竞
争, 再取消偏向锁状态, 进入轻量级锁状态.

 




 

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

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

相关文章

【Kafka】【三】安装Kafka服务器

Kafka基本知识 Kafka介绍 Kafka是最初由Linkedin公司开发,是⼀个分布式、⽀持分区的(partition)、多副本的 (replica),基于zookeeper协调的分布式消息系统,它的最⼤的特性就是可以实时的处理 …

蓝牙安全(AES-CCM)

目录 AES-CCM CCM规范加密过程 CCM规范解密认证过程 formatting函数 counter generation函数 蓝牙AES-CCM加密流程 参考文献 AES-CCM Advanced Encryption Standard-Counter with Cipher Block Chaining-Message Authentication Code 自蓝牙4.1起蓝牙的加密算法开始采…

RabbitMQ-其他问题

一、幂等性问题:消费者在消费MQ中的消息时,MQ已把消息发送给消费者,消费者在给MQ返回ACK时网络中断,故MQ未收到确认消息,该消息会重新发送给其他消费者,或者在网络重连后再次发送给消费者,但实际…

第三章虚拟机的克隆,快照,迁移删除

1.虚拟机的克隆 如果你已经安装了一台linux操作系统,你还想再更多的,没有必要再重新安装,你只需要克 隆就可以,看演示。 方式1,直接拷贝一份安装好的虚拟机文件,再用虚拟机打开这个文件方式2,使用vmware的…

企业三要素核验API接口,你了解多少?

企业三要素核验API接口是指哪些要素?企业三要素是一种有关企业实名认证的应用程序接口也称API,企业的名称、统一社会信用代码和法人代表姓名统称企业三要素。企业三要素核验API接口的资源来自国家工商总局数据库,通过数据库资料三个要素进行核…

字母板上的路径 题解,力扣官方出来挨打(小声)

字母板上的路径 我们从一块字母板上的位置 (0, 0) 出发,该坐标对应的字符为 board[0][0]。 在本题里,字母板为board [“abcde”, “fghij”, “klmno”, “pqrst”, “uvwxy”, “z”],如下所示。 我们可以按下面的指令规则行动&#xff1a…

Dubbo基本原理和用法讲解

Dubbo基本原理和用法讲解 序言:学习一项新技术,一般从是什么、为什么、怎么用三个方面进行学习。本篇文章也不例外,笔者将从Dubbo是什么?、为什么会产生Dubbo技术?、如何在项目中使用Dubbo技术。最后,笔者…

基于springboot+vue的宠物商城系统(前后端分离)

博主主页:猫头鹰源码 博主简介:Java领域优质创作者、CSDN博客专家、公司架构师、全网粉丝5万、专注Java技术领域和毕业设计项目实战 主要内容:毕业设计(Javaweb项目|小程序等)、简历模板、学习资料、面试题库、技术咨询 文末联系获取 项目介绍…

Fluent Python 笔记 第 6 章 使用一等函数实现设计模式

虽然设计模式与语言无关,但这并不意味着每一个模式都能在每一门语言中使用。1996 年,Peter Norvig 在题为“Design Patterns in Dynamic Languages”(http://norvig.com/design- patterns/)的演讲中指出,Gamma 等人合著的《设计模式:可复用面…

序列化和反序列化~如何实现自定义协议?jsoncpp的使用

目录 序列化反序列化的概念 为什么要进行序列化和反序列化? 自定义协议实现业务 jsoncpp实现序列化和反序列化 序列化: 反序列化: 自定义协议jsoncpp实现简易计算器服务整体逻辑 Server.cc Client.cc 运行结果 序列化反序列化的概念…

重建control遗漏数据文件,reseltogs报ORA-1555错误处理----惜分飞

又一客户,误删除oracle redo导致数据库无法正常启动,自己尝试重建ctl,结果遗漏部分oracle数据文件并且尝试过resetlogs,导致部分文件resetlogs scn不一致.导致重建ctl失败Fri Feb 10 12:41:20 2023CREATE CONTROLFILE REUSE DATABASE "orcl"RESETLOGS NOARCHIVELOG M…

GO 中的 init 函数

前言 go 语言中有一个非常神奇的函数 init ,它可以在所有程序执行开始前被执行,并且每个 package 下面可以存在多个 init 函数,我们一起来看看这个奇怪的 init 函数。 init 特性 init 函数在 main 函数之前执行,并且是自动执行&#xff1b…

Docker网络实现原理

目录 1 Docker网络实现原理 1.2 为容器创建端口映射 方法一:随机映射端口(从32768开始) 方法二:指定映射端口 1.3 查看容器的输出和日志信息 二 Docker的网络模式 2.1 Docker的网络模式(41) 2.2 查看…

【大数据趋势】2月12日 货币发动机牵着港股和A股走,历史不会简单重演,但是会用类似的方法让人再踏入同一条河流

行情核心源头之一 : 离岸人民币和美元趋势历史对比,预示着一个阶段底部正在形成中 历史总是很容易忘记,应该很少有人记得18年发生了什么。还是让大数据程序来对比一下。【红色标记1】RSI预示着價格強度的动能情况,同样是达到了一个高点&…

2021 WAIC 世界人工智能大会参会总结

前言 2021 年世界人工智能大会(WAIC)于2021年7月7日至10日在上海世博展览馆举办,本届大会继续秉持「智联世界」的理念,以「众智成城」为主题,促进全球人工智能创新思想、技术、应用、人才和资本的集聚和交流&#xff…

JS逆向案例分享----prototype的妙用

方向不对,努力白费。爬虫也是如此。今天分享的案例很能说明问题。目标:某集团公司采购信息aHR0cHM6Ly9lYy5taW5tZXRhbHMuY29tLmNuL29wZW4vaG9tZS9wdXJjaGFzZS1pbmZv浏览器抓包,请求载荷里就一个加密参数param全局搜索“param:”,有…

【计组】DMA、数据完整性--《深入浅出计算机组成原理》(十三)

目录 一、DMA (一)理解DMA,一个协处理器 (二) Kafka 的实现原理 二、数据的完整性 (一)单比特翻转:软件解决不了的硬件错误 (二)海明码 1、海明码的纠错…

《狂飙》壁纸大嫂如此惊艳,做成日历壁纸天天看(7)

小朋友们好,大朋友们好!我是猫妹!话说兔年春节期间,一部反黑反腐电视剧横空出世,收视率和口碑都有不错的成绩!这部电视剧叫《狂飙》!你看了吗?我没看!不过这丝毫不影响它…

C语言fread和fwrite的用法详解

fgets() 有局限性,每次最多只能从文件中读取一行内容,因为 fgets() 遇到换行符就结束读取。如果希望读取多行内容,需要使用 fread() 函数;相应地写入函数为 fwrite()。对于 Windows 系统,使用 fread() 和 fwrite() 时应…

Python+OpenCV 简单实现人脸检测多个和人脸识别 2(附代码)

如果dilb和face_recognition第三方包安装失败,请移步到Python 解决dilb和face_recognition第三方包安装失败_水w的博客-CSDN博客 上篇请移步到Pythondilb 简单实现人脸检测(附代码)_水w的博客-CSDN博客 本篇是在上篇的工作基础上进行的。 目…