synchronized原理
目录
一、基本特点
二、加锁过程
2.1、偏向锁
2.2、轻量级锁
2.3、重量级锁
三、其它的优化操作
3.1、锁消除
3.2、锁粗化
一、基本特点
synchronized有以下特性:
- 开始是乐观锁,如果锁冲突频繁,就转换为悲观锁。
- 开始是轻量级锁,如果锁被持有的时间较长,就转换为重量级锁。
- 实现轻量级锁的时候大概率用到的自旋锁策略。
- 是一种不公平锁。
- 是一种可重入锁。
- 不是读写锁。
二、加锁过程
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有以下特性: 开始是乐观锁,如果锁冲突频繁,就转换为悲观锁。开始是轻量级锁,…...
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️⃣总结附࿱…...
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…...
页面渲染流程与性能优化
页面渲染流程与性能优化详解(完整版) 一、现代浏览器渲染流程(详细说明) 1. 构建DOM树 浏览器接收到HTML文档后,会逐步解析并构建DOM(Document Object Model)树。具体过程如下: (…...
【算法训练营Day07】字符串part1
文章目录 反转字符串反转字符串II替换数字 反转字符串 题目链接:344. 反转字符串 双指针法,两个指针的元素直接调转即可 class Solution {public void reverseString(char[] s) {int head 0;int end s.length - 1;while(head < end) {char temp …...
数据链路层的主要功能是什么
数据链路层(OSI模型第2层)的核心功能是在相邻网络节点(如交换机、主机)间提供可靠的数据帧传输服务,主要职责包括: 🔑 核心功能详解: 帧封装与解封装 封装: 将网络层下发…...
Spring AI 入门:Java 开发者的生成式 AI 实践之路
一、Spring AI 简介 在人工智能技术快速迭代的今天,Spring AI 作为 Spring 生态系统的新生力量,正在成为 Java 开发者拥抱生成式 AI 的最佳选择。该框架通过模块化设计实现了与主流 AI 服务(如 OpenAI、Anthropic)的无缝对接&…...
使用 Streamlit 构建支持主流大模型与 Ollama 的轻量级统一平台
🎯 使用 Streamlit 构建支持主流大模型与 Ollama 的轻量级统一平台 📌 项目背景 随着大语言模型(LLM)的广泛应用,开发者常面临多个挑战: 各大模型(OpenAI、Claude、Gemini、Ollama)接口风格不统一;缺乏一个统一平台进行模型调用与测试;本地模型 Ollama 的集成与前…...
动态 Web 开发技术入门篇
一、HTTP 协议核心 1.1 HTTP 基础 协议全称 :HyperText Transfer Protocol(超文本传输协议) 默认端口 :HTTP 使用 80 端口,HTTPS 使用 443 端口。 请求方法 : GET :用于获取资源,…...
C++课设:简易日历程序(支持传统节假日 + 二十四节气 + 个人纪念日管理)
名人说:路漫漫其修远兮,吾将上下而求索。—— 屈原《离骚》 创作者:Code_流苏(CSDN)(一个喜欢古诗词和编程的Coder😊) 专栏介绍:《编程项目实战》 目录 一、为什么要开发一个日历程序?1. 深入理解时间算法2. 练习面向对象设计3. 学习数据结构应用二、核心算法深度解析…...
PHP 8.5 即将发布:管道操作符、强力调试
前不久,PHP宣布了即将在 2025 年 11 月 20 日 正式发布的 PHP 8.5!作为 PHP 语言的又一次重要迭代,PHP 8.5 承诺带来一系列旨在提升代码可读性、健壮性以及开发者效率的改进。而更令人兴奋的是,借助强大的本地开发环境 ServBay&am…...
人工智能 - 在Dify、Coze、n8n、FastGPT和RAGFlow之间做出技术选型
在Dify、Coze、n8n、FastGPT和RAGFlow之间做出技术选型。这些平台各有侧重,适用场景差异显著。下面我将从核心功能定位、典型应用场景、真实体验痛点、选型决策关键点进行拆解,并提供具体场景下的推荐方案。 一、核心功能定位速览 平台核心定位技术栈亮…...
高分辨率图像合成归一化流扩展
大家读完觉得有帮助记得关注和点赞!!! 1 摘要 我们提出了STARFlow,一种基于归一化流的可扩展生成模型,它在高分辨率图像合成方面取得了强大的性能。STARFlow的主要构建块是Transformer自回归流(TARFlow&am…...
