当前位置: 首页 > news >正文

synchronized原理

目录

一、基本特点

二、加锁过程 

2.1、偏向锁

2.2、轻量级锁

2.3、重量级锁 

三、其它的优化操作 

3.1、锁消除

3.2、锁粗化 


一、基本特点

synchronized有以下特性:

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

二、加锁过程 

JVM会将synchronizde锁分为无锁,偏向锁,轻量级锁,重量级锁状态。根据锁的激烈程度,自动进行锁升级。

 

举个栗子:

1、我们在学校每天早上去图书馆学习,‘坤坤’已经在图书馆学习时长两年半,所以坤坤每天都要起一个大早去学习,他真的很努力,所以他到了图书馆基本上没有人,坤坤可以随便坐。

2、过了一个小时,有真爱粉来图书馆找咯咯一起学习,真爱粉就把书本放在桌子上,标志着这个地方有人了,给自己占了个位置,其他人不准用。

3、 随着真爱粉越来越多,学习的人也越来越多,图书馆也就慢慢有了竞争,于是有的同学的书本就会被放在一边,自己的位置被别人占了,真爱粉回来之后发现座位被占了,周围也满了,她就站在原地一直等,相当于自旋状态,可以在第一时间发现空余作为,效率比较高。

4、听说咯咯每天都去图书馆学习,小黑子门也急了,他们也要去图书占位,于是图书馆的座位竞争越来越激烈,高峰时期就要排队(阻塞队列),阻塞队列也就越来越长,每个人都要检查锁,如果没有可用的锁就要去排队,对于synchronized来说,相当于是重量级锁,会调用内核态的加锁指令,来完成获取锁操作。

2.1、偏向锁

第一个尝试加锁的线程,会优先进入偏向锁。

偏向锁不是真的”加锁“,而是给对象头加了一个”偏向锁标记“,记录这个锁属于哪个线程。

如果后续没有其它线程来竞争该锁,那么就不用进行其它同步操作了(避免了加锁解锁的资源开销)如果后续有其它线程来竞争该锁,那就取消原来的偏向锁状态,进入一般的轻量级锁状态。

偏向锁的本质上相当于”延迟加锁“,能不加锁就不加锁,避免资源的浪费。

但是该做标记还是要做,否则无法区分何时真正加锁。

2.2、轻量级锁

随着其它线程进入竞争,偏向锁状态被解除,进入轻量级锁状态

此处的轻量级锁就通过CAS(自旋)来实现.

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

自旋操作是让CPU一直空转,比较浪费资源,因此自旋状态不会一直持续进行,而是达到一定时间(重试次数),就不再自旋,也就是”自适应“。

2.3、重量级锁 

如果锁竞争进一步激烈,自旋不能快速获取到锁,就会膨胀为重量级锁。

此处的重量级锁就是指内核提供的mutex.

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

打印类的布局:


import org.openjdk.jol.info.ClassLayout;/*** 打印类的布局*/
public class Exe_01 {//定义变量private int count = 100;private long count1 = 200;private String hello = "";private TextExe_01 textExe_01 = new TextExe_01();public static void main(String[] args) throws InterruptedException {//创建一个对象的实例Object obj = new Object();//打印实例布局System.out.println("=====任意object对象布局,起初无锁状态");System.out.println(ClassLayout.parseInstance(obj).toPrintable());System.out.println("延迟三秒开启偏向锁");//延迟3秒开启偏向锁Thread.sleep(3000);//创建本类的实例Exe_01 monitor = new Exe_01();//打印实例布局,查看锁状态System.out.println("=====打印实例布局,注意查看锁状态为偏向锁");System.out.println(ClassLayout.parseInstance(monitor).toPrintable());//调用hashCode后,保存hashCode的值monitor.hashCode();//观察现象System.out.println(ClassLayout.parseInstance(monitor).toPrintable());System.out.println("==============================");System.out.println("synchronized加锁");//加锁后观察锁信息synchronized (monitor) {System.out.println("第一层synchronized加锁后");System.out.println(ClassLayout.parseInstance(monitor).toPrintable());//锁重入,观察锁信息synchronized (monitor) {System.out.println("第二层synchronized加锁后,锁重入");System.out.println(ClassLayout.parseInstance(monitor).toPrintable());}//释放里层的锁System.out.println("释放内层锁后");System.out.println(ClassLayout.parseInstance(monitor).toPrintable());}//释放所有锁之后System.out.println("=========释放所有锁========");System.out.println(ClassLayout.parseInstance(monitor).toPrintable());System.out.println("==== 多个线程参与锁竞争,观察锁状态");//强制执行垃圾回收System.gc();//观察GC计数System.out.println("+++++++调用GC后查看age的值");System.out.println(ClassLayout.parseInstance(monitor).toPrintable());//打印类布局,调用不同的方法查看System.out.println("+++++查看类布局");System.out.println(ClassLayout.parseClass(Exe_01.class).toPrintable());//打印类对象布局System.out.println("+++++查看类对象布局");System.out.println(ClassLayout.parseInstance(Exe_01.class).toPrintable());synchronized (Exe_01.class) {//加锁后的类对象System.out.println("+++++对类对象加锁后,不同的对象获取锁,观察锁升级为thin lock");System.out.println(ClassLayout.parseInstance(Exe_01.class).toPrintable());}//释放锁之后的类对象System.out.println("+++++释放锁后");System.out.println(ClassLayout.parseInstance(Exe_01.class).toPrintable());System.out.println("+++++多个锁线程参与锁竞争,观察锁状态+++++");Thread thread1 = new Thread(() -> {synchronized (monitor) {System.out.println("=== 在线程A 中获取锁,参与锁竞争,当前只有线程A 竞争锁,轻度锁竞争");System.out.println(ClassLayout.parseInstance(monitor).toPrintable());}});thread1.start();// 休眠一会,不与线程A 激烈竞争Thread.sleep(100);Thread thread2 = new Thread(() -> {synchronized (monitor) {System.out.println("=== 在线程B 中获取锁,与其他线程进行锁竞争");System.out.println(ClassLayout.parseInstance(monitor).toPrintable());}});thread2.start();// 不休眠直接竞争锁,产生激烈竞争System.out.println("==== 不休眠直接竞争锁,产生激烈竞争");synchronized (monitor) {// 加锁后的类对象System.out.println("==== 与线程B 产生激烈的锁竞争,观察锁状态为fat lock");System.out.println(ClassLayout.parseInstance(monitor).toPrintable());}// 休眠一会释放锁后Thread.sleep(100);System.out.println("==== 释放锁后");System.out.println(ClassLayout.parseInstance(monitor).toPrintable());System.out.println("===========================================================================================");// 调用hashCode后才保存hashCode的值monitor.hashCode();// 调用hashCode后观察现象System.out.println("==== 调用hashCode后查看hashCode的值");System.out.println(ClassLayout.parseInstance(monitor).toPrintable());// 强制执行垃圾回收System.gc();// 观察GC计数System.out.println("==== 调用GC后查看age的值");System.out.println(ClassLayout.parseInstance(monitor).toPrintable());// 打印类布局,注意调用的方法不同System.out.println("==== 查看类布局");System.out.println(ClassLayout.parseClass(Exe_01.class).toPrintable());// 打印类对象布局System.out.println("==== 查看类对象布局");System.out.println(ClassLayout.parseInstance(Exe_01.class).toPrintable());}
}class TextExe_01 {}

结果分析: 

里面的参数类型可参考这篇文章-Java对象的内存布局 - JaJian - 博客园 (cnblogs.com) 

 

 

三、其它的优化操作 

3.1、锁消除

synchronized的一种优化策略

synchronized是手动添加的自己获取到锁的处理逻辑,什么时候加,加在哪里,JVM无法去管理,但是在代码编译和运行的时候,JVM可以知道程序是读变量,还是写变量。

如果我们手动对所有的读操作都加了synchronized关键字,但是又没有写操作,,那么这个时候JVM就认为这个锁是多余的,那么synchronized就不会去枷锁了,这种现象就叫做”锁消除“。

注意

多线程状态下,多线程对同一个变量进行修改才会有线程安全问题,而多线程对同一个变量读取没有线程安全问题。

synchronized只有100%确定不需要锁的时候,才会进行锁消除优化 。

3.2、锁粗化 

一段逻辑中如果出现多次加锁解锁,编译器JVM就会自动进行锁粗话。

 真实的代码执行过程,从方法1到方法5全部都执行完,才结束。

 如上面的流程中每一个方法都会有一个申请锁资源,释放锁资源的流程。

那么对于这种现象,synchronized就会认为从头到尾只需要获取一次锁资源,中途不会释放锁。

这个现象就叫“锁粗化”,从方法级别(细粒度变成了业务级别的粗粒度)

相关文章:

synchronized原理

目录 一、基本特点 二、加锁过程 2.1、偏向锁 2.2、轻量级锁 2.3、重量级锁 三、其它的优化操作 3.1、锁消除 3.2、锁粗化 一、基本特点 synchronized有以下特性: 开始是乐观锁,如果锁冲突频繁,就转换为悲观锁。开始是轻量级锁&#xff0c…...

10G光模块能兼容千兆光口吗

当涉及到光网络设备和光模块的兼容性时,确保正确的匹配是至关重要的。本期文章内容,我们将探讨10G光模块与千兆光口之间的兼容性。 一、10G光模块和千兆光口的基本概念 首先,我们需要了解10G光模块和千兆光口的基本概念。10G光模块是一种用…...

css 显示省略号 和 动态显示省略号

省略是非常常见的功能。 简单的实现省略号 下面的代码就可以实现省略号,超过宽度的时候就会出现省略号 .text-name{//宽高是一定要设置的不然是会无效延伸的width: 200rpx;overflow: hidden;white-space: nowrap;text-overflow: ellipsis;}稍微复杂点的情况&#…...

LeetCode 1253. 重构 2 行二进制矩阵

【LetMeFly】1253.重构 2 行二进制矩阵 力扣题目链接:https://leetcode.cn/problems/reconstruct-a-2-row-binary-matrix/ 给你一个 2 行 n 列的二进制数组: 矩阵是一个二进制矩阵,这意味着矩阵中的每个元素不是 0 就是 1。第 0 行的元素之…...

【八股】【C++】内存

这里写目录标题 内存空间分配new和delete原理C有几种newmalloc / free 与 new / delete区别malloc和free原理?delete和delete[]区别?C内存泄漏malloc申请的存储空间能用delete释放吗?malloc、calloc函数、realloc函数C中浅拷贝与深拷贝栈和队列的区别C里…...

数据库G等待

> db^Cgbasedbtpc:~$ dbaccess db10 -Database selected.> call insert_t();Routine executed.Elapsed time: 811.630 sec 磁盘逻辑日志,无BUF库> ^Cgbasedbtpc:~$ gbasedbtpc:~$ dbaccess db10 -Database selected.> call insert_t();Routine executed.Elapse…...

PCB封装设计指导(一)基础知识

PCB封装设计指导(一)基础知识 PCB封装是PCB设计的基础,也是PCB最关键的部件之一,尺寸需要非常准确且精确,关系到设计,生产加工,贴片等后续一系列的流程。 下面以Allegro为例介绍封装创建前的一些基础知识 1.各个psm文件代表什么 mechanical symbol 是.bsm Package sy…...

Flask框架之Restful--介绍--下载--基本使用

目录 Restful 概念 架构的主要原则 适用场景 协议 数据传输格式 url链接规则 HTTP请求方式 状态码 Restful的基本使用 介绍 优势 缺点 安装 基本使用 注意 Restful 概念 RESTful(Representational State Transfer)是一种用于设计网络应用…...

2023年上海市浦东新区网络安全管理员决赛理论题样题

目录 一、判断题 二、单选题 三、多选题 一、判断题 1.等保1.0至等保2.0从信息系统拓展为网络和信息系统。 正确 (1)保护对象改变 等保1.0保护的对象是信息系统,等保2.0增加为网络和信息系统,增加了云计算、大数据、工业控制系统、物联网、移动物联技术、网络基础…...

SQL语言的四大组成部分——DCL(数据控制语言)

1️⃣前言 SQL语言中的DCL(Data Control Language)是一组用于控制数据库用户访问权限的语言,主要包括GRANT、REVOKE、DENY等关键字。 文章目录 1️⃣前言2️⃣DCL语言3️⃣GRANT关键字4️⃣REVOKE关键字5️⃣DENY关键字6️⃣总结附&#xff1…...

ChatGPT新功能曝光:可记住用户信息、上传文件和工作区

🦉 AI新闻 🚀 ChatGPT新功能曝光:可记住用户信息、上传文件和工作区 摘要:一张神秘截图曝光了ChatGPT新功能,包括可记住用户信息的"My profile"、上传和管理文件的"My files"以及可以让AI使用不…...

【Unity编辑器扩展】(三)PSD转UGUI Prefab, 一键拼UI解放美术/程序(完结)

工具效果: 第一步,把psd图层转换为可编辑的节点树,并自动解析UI类型、自动绑定UI子元素: 第二步, 点击“生成UIForm"按钮生成UI预制体 (若有UI类型遗漏可在下拉菜单手动点选UI类型): 验证一键生成UI效果: 书接上…...

SpringBoot开发Restful风格的接口实现CRUD功能

基于SpringBoot开发一个Restful接口 前言一、什么是SpringBoot?二、实战---基于SpringBoot开发一个Restful接口1.开发前的准备工作1.1 添加相关依赖 (pom文件) 1.2 创建相关数据库和表1.3 数据库配置文件 2.实战开发---代码逻辑2.1 实体类2.2…...

【Servlet学习三】实现一个内存版本的简易计算器~

目录 一、方式1:使用form表单的形式(不推荐) 🌈1、前端代码:HTML文件 🌈2、后端代码:Calculator_form.java文件 🌈3、最终效果 二、方式2:使用ajax形式(…...

Linux c语言获取本机网关 ip 地址

文章目录 前言一、获取本机网关 ip 地址1.1 代码示例1.2 代码详解介绍 二、使用Netlink套接字实时监控网络事件2.1 简介2.2 示例代码 前言 这篇文章写了获取本机的ip地址和子网掩码:Linux c语言获取本机 ip、子网掩码 一、获取本机网关 ip 地址 使用Netlink套接字…...

nginx部署本地项目如何让异地公网访问?服务器端口映射配置!

接触过IIS或apache的小伙伴们,对nginx是比较容易理解的,nginx有点类似,又有所差异,在选择使用时根据自己本地应用场景来部署使用即可。通过一些对比可能会更加清楚了解: 1.nginx是轻量级,比apache占用更少…...

云时代已至,新一代数据分析平台是如何实现的?

2023 年 5 月,由 Stackoverflow 发起的 2023 年度开发者调查数据显示,PostgreSQL 已经超越 MySQL 位居第一,成为开发人员首选。PostgreSQL 在国内的热度也越来越高。6 月 17 日,PostgreSQL 数据库技术峰会在成都顺利召开。本次大会…...

【C#】简单聊下Framework框架下的事务

框架用的多了,之前版本的事务都忘记了。本次简单聊下.net framework 4.8框架下本身的事务 目录 1、SqlClient2、TransactionScope3、引用 1、SqlClient 在 C# 中,使用 using 块可以方便地实现对资源的自动释放,但它不适用于实现事务处理。为…...

asyncPool并发执行请求函数

asyncPool应用场景 一个不太常见的极端场景,当我们为了某个操作需要发生异步请求的时候,等待所有异步请求都完成时进行某些操作。这个时候我们不在简简单单的发送 1 - 2 个请求而是 5 - 10个(其实极端场景式 很多很多个请求,这个…...

Ubuntu 22.04上安装NFS服务

1、使用如下命令安装NFS服务端软件: # 在主机上运行以下命令 orangepiorangepi5:~$ sudo apt install nfs-server 2、在配置NFS时需要使用用户uid和组gid,可以使用id命令查看 # 在主机上运行id命令 orangepiorangepi5:~$ id uid1000(orangepi) gid100…...

第19节 Node.js Express 框架

Express 是一个为Node.js设计的web开发框架,它基于nodejs平台。 Express 简介 Express是一个简洁而灵活的node.js Web应用框架, 提供了一系列强大特性帮助你创建各种Web应用,和丰富的HTTP工具。 使用Express可以快速地搭建一个完整功能的网站。 Expre…...

蓝牙 BLE 扫描面试题大全(2):进阶面试题与实战演练

前文覆盖了 BLE 扫描的基础概念与经典问题蓝牙 BLE 扫描面试题大全(1):从基础到实战的深度解析-CSDN博客,但实际面试中,企业更关注候选人对复杂场景的应对能力(如多设备并发扫描、低功耗与高发现率的平衡)和前沿技术的…...

【算法训练营Day07】字符串part1

文章目录 反转字符串反转字符串II替换数字 反转字符串 题目链接&#xff1a;344. 反转字符串 双指针法&#xff0c;两个指针的元素直接调转即可 class Solution {public void reverseString(char[] s) {int head 0;int end s.length - 1;while(head < end) {char temp …...

音视频——I2S 协议详解

I2S 协议详解 I2S (Inter-IC Sound) 协议是一种串行总线协议&#xff0c;专门用于在数字音频设备之间传输数字音频数据。它由飞利浦&#xff08;Philips&#xff09;公司开发&#xff0c;以其简单、高效和广泛的兼容性而闻名。 1. 信号线 I2S 协议通常使用三根或四根信号线&a…...

LOOI机器人的技术实现解析:从手势识别到边缘检测

LOOI机器人作为一款创新的AI硬件产品&#xff0c;通过将智能手机转变为具有情感交互能力的桌面机器人&#xff0c;展示了前沿AI技术与传统硬件设计的完美结合。作为AI与玩具领域的专家&#xff0c;我将全面解析LOOI的技术实现架构&#xff0c;特别是其手势识别、物体识别和环境…...

uniapp 小程序 学习(一)

利用Hbuilder 创建项目 运行到内置浏览器看效果 下载微信小程序 安装到Hbuilder 下载地址 &#xff1a;开发者工具默认安装 设置服务端口号 在Hbuilder中设置微信小程序 配置 找到运行设置&#xff0c;将微信开发者工具放入到Hbuilder中&#xff0c; 打开后出现 如下 bug 解…...

nnUNet V2修改网络——暴力替换网络为UNet++

更换前,要用nnUNet V2跑通所用数据集,证明nnUNet V2、数据集、运行环境等没有问题 阅读nnU-Net V2 的 U-Net结构,初步了解要修改的网络,知己知彼,修改起来才能游刃有余。 U-Net存在两个局限,一是网络的最佳深度因应用场景而异,这取决于任务的难度和可用于训练的标注数…...

Ubuntu系统复制(U盘-电脑硬盘)

所需环境 电脑自带硬盘&#xff1a;1块 (1T) U盘1&#xff1a;Ubuntu系统引导盘&#xff08;用于“U盘2”复制到“电脑自带硬盘”&#xff09; U盘2&#xff1a;Ubuntu系统盘&#xff08;1T&#xff0c;用于被复制&#xff09; &#xff01;&#xff01;&#xff01;建议“电脑…...

HTML前端开发:JavaScript 获取元素方法详解

作为前端开发者&#xff0c;高效获取 DOM 元素是必备技能。以下是 JS 中核心的获取元素方法&#xff0c;分为两大系列&#xff1a; 一、getElementBy... 系列 传统方法&#xff0c;直接通过 DOM 接口访问&#xff0c;返回动态集合&#xff08;元素变化会实时更新&#xff09;。…...

Mysql故障排插与环境优化

前置知识点 最上层是一些客户端和连接服务&#xff0c;包含本 sock 通信和大多数jiyukehuduan/服务端工具实现的TCP/IP通信。主要完成一些简介处理、授权认证、及相关的安全方案等。在该层上引入了线程池的概念&#xff0c;为通过安全认证接入的客户端提供线程。同样在该层上可…...