【Synchronized】不同的使用场景和案例

【Synchronized】不同的使用场景和案例

  • 【一】锁的作用范围与锁对象
    • 【1】实例方法(对象锁)
    • 【2】静态方法(类锁)
    • 【3】代码块(显式指定锁对象)
    • 【4】类锁(通过Class对象显式锁定)
  • 【二】核心区别总结
  • 【三】关键注意事项
    • 【1】内存可见性
    • 【2】避免死锁
    • 【3】锁粒度过大问题
  • 【四】测试案例补充
  • 【五】原理
    • 【1】Synchronized锁原理
      • (1)对象头
      • (2)监视器(Monitor)
      • (3)字节码层面的实现
    • 【2】锁升级原理
      • (1)无锁状态
      • (2)偏向锁
      • (3)轻量级锁
      • (4)重量级锁
    • 【3】总结

【一】锁的作用范围与锁对象

【1】实例方法(对象锁)

(1)锁对象:当前实例(this)。
(2)作用范围:同一实例的多个线程访问同步方法时互斥;不同实例的同步方法互不影响。
(3)测试案例:

public class BankAccount {private int balance = 1000;public synchronized void withdraw(int amount) {if (balance >= amount) {balance -= amount;}}
}// 测试代码
BankAccount account1 = new BankAccount();
BankAccount account2 = new BankAccount();// 线程1操作account1的withdraw方法(互斥)
new Thread(() -> account1.withdraw(500)).start();
// 线程2操作account1的withdraw方法(被阻塞)
new Thread(() -> account1.withdraw(500)).start();
// 线程3操作account2的withdraw方法(不受影响)
new Thread(() -> account2.withdraw(500)).start();

(4)结果:线程1和线程2对account1的操作互斥;线程3与account1的操作无关

【2】静态方法(类锁)

(1)锁对象:类的Class对象(如BankAccount.class)。
(2)作用范围:所有实例调用静态同步方法时互斥。
(3)测试案例:

public class OrderService {private static int orderCount = 0;public static synchronized void generateOrder() {orderCount++;}
}// 测试代码
OrderService instanceA = new OrderService();
OrderService instanceB = new OrderService();// 线程1调用静态方法(互斥)
new Thread(() -> OrderService.generateOrder()).start();
// 线程2调用静态方法(被阻塞)
new Thread(() -> OrderService.generateOrder()).start();
// 线程3通过实例调用静态方法(同样被阻塞)
new Thread(() -> instanceA.generateOrder()).start();

(4)结果:所有线程调用generateOrder()都会竞争同一把类锁,无论通过类名还是实例调用。

【3】代码块(显式指定锁对象)

(1)锁对象:任意显式指定的对象(如实例变量、类对象)。
(2)作用范围:仅同步代码块内的操作,锁粒度更细。
(3)测试案例:

public class CacheManager {private final Object lock = new Object();private int cacheSize = 0;public void updateCache() {// 非同步代码System.out.println("非同步操作");synchronized(lock) { // 显式锁定lock对象cacheSize++;}}
}// 测试代码
CacheManager manager = new CacheManager();
// 线程1和线程2竞争lock对象的锁
new Thread(() -> manager.updateCache()).start();
new Thread(() -> manager.updateCache()).start();

(4)结果:仅代码块内的cacheSize++操作互斥,其他代码可并发执行347。

【4】类锁(通过Class对象显式锁定)

(1)锁对象:类的Class对象(如ClassName.class)。
(2)作用范围:与静态方法锁效果一致,但可用于非静态方法中。
(3)测试案例:

public class LockDemo {public void classLockBlock() {synchronized(LockDemo.class) { // 显式锁定类对象// 同步代码}}
}// 线程1和线程2调用classLockBlock方法时互斥
LockDemo obj1 = new LockDemo();
LockDemo obj2 = new LockDemo();
new Thread(() -> obj1.classLockBlock()).start();
new Thread(() -> obj2.classLockBlock()).start();

(4)结果:不同实例调用classLockBlock时仍互斥,因为锁的是LockDemo.class

【二】核心区别总结

锁类型 锁对象 作用范围 适用场景
实例方法(对象锁) 当前实例(this) 同一实例的同步方法 保护实例变量(如账户余额)
静态方法(类锁) 类的Class对象 所有实例的静态方法 保护静态变量(如全局计数器)
显式对象锁 指定对象(如lock) 锁定指定对象的同步代码块 细粒度控制(如缓存更新)
显式类锁 ClassName.class 跨实例同步(与静态方法锁等效) 需要全局同步的非静态方法逻辑

【三】关键注意事项

【1】内存可见性

实例锁仅保证同一实例内的变量可见性。
类锁保证所有实例间的变量可见性(因Class对象在方法区共享)。

【2】避免死锁

按固定顺序获取锁(如先锁A再锁B)。
避免嵌套锁(如synchronized方法内调用另一个synchronized方法时需谨慎)。

【3】锁粒度过大问题

错误示例:将耗时操作(如IO)放在同步方法内。
优化方案:仅同步关键代码块

【四】测试案例补充

场景:对象锁与类锁的互斥性测试

public class MixedLockDemo {public synchronized void instanceMethod() {// 实例锁}public static synchronized void staticMethod() {// 类锁}
}// 线程1调用实例方法,线程2调用静态方法
MixedLockDemo obj = new MixedLockDemo();
new Thread(() -> obj.instanceMethod()).start();  // 锁obj实例
new Thread(() -> MixedLockDemo.staticMethod()).start();  // 锁MixedLockDemo.class

结果:两个线程不会互斥,因为实例锁和类锁是独立的

【五】原理

【1】Synchronized锁原理

(1)对象头

在 Java 中,每个对象都有一个对象头(Object Header),对象头中包含了一些与对象自身相关的信息,如哈希码、分代年龄等,同时也包含了锁的相关信息。对象头的结构在不同的 JVM 实现中可能会有所不同,但通常会包含一个 Mark Word 字段,这个字段用于存储对象的锁状态、哈希码等信息。

(2)监视器(Monitor)

synchronized 关键字的底层实现依赖于监视器(Monitor)。监视器是 Java 中实现同步的基础,它是一个同步工具,相当于一个特殊的房间,线程进入这个房间就相当于获得了锁,其他线程则需要等待。每个 Java 对象都可以关联一个监视器,当一个线程试图访问被 synchronized 修饰的代码块或方法时,它会首先尝试获取该对象的监视器。

(3)字节码层面的实现

当使用 synchronized 修饰代码块时,编译后的字节码会包含 monitorenter 和 monitorexit 指令。例如:

public class SynchronizedExample {public void test() {synchronized (this) {// 同步代码块}}
}

编译后的字节码中会包含如下指令:

monitorenter
// 同步代码块的字节码
monitorexit

(1)monitorenter 指令
当线程执行到 monitorenter 指令时,它会尝试获取对象的监视器。如果监视器的进入计数器为 0,表示该监视器没有被其他线程持有,当前线程可以获取该监视器,并将进入计数器加 1。如果监视器已经被其他线程持有,当前线程会被阻塞,直到该监视器被释放。

(2)monitorexit 指令
当线程执行到 monitorexit 指令时,它会将监视器的进入计数器减 1。当进入计数器为 0 时,表示当前线程已经释放了该监视器,其他线程可以尝试获取该监视器。

当使用 synchronized 修饰方法时,编译后的字节码会在方法的访问标志中设置 ACC_SYNCHRONIZED 标志。当线程调用该方法时,会自动检查该标志,如果设置了该标志,线程会首先尝试获取该方法所属对象的监视器,然后再执行方法体。

【2】锁升级原理

在 Java 6 之前,synchronized 是一个重量级锁,性能较低。为了提高性能,Java 6 引入了锁升级机制,使得 synchronized 的性能有了显著提升。锁升级的过程是一个逐步升级的过程,从无锁状态开始,经过偏向锁、轻量级锁,最终升级为重量级锁。

(1)无锁状态

对象刚被创建时,处于无锁状态,Mark Word 中存储的是对象的哈希码、分代年龄等信息。

(2)偏向锁

(1)原理:偏向锁是为了在没有竞争的情况下减少锁的获取和释放带来的性能开销。当一个线程第一次访问被 synchronized 修饰的代码块或方法时,会在对象头的 Mark Word 中记录该线程的 ID,这个过程称为偏向锁的获取。以后该线程再次进入该同步代码块时,无需进行任何同步操作,直接进入代码块执行,这样可以避免频繁的锁获取和释放操作,提高性能。

(2)升级条件:当有其他线程尝试竞争该偏向锁时,偏向锁会升级为轻量级锁。

(3)轻量级锁

(1)原理:当线程获取轻量级锁时,会在当前线程的栈帧中创建一个锁记录(Lock Record),并将对象头的 Mark Word 复制到锁记录中。然后,线程尝试使用 CAS(Compare-And-Swap)操作将对象头的 Mark Word 替换为指向锁记录的指针。如果替换成功,说明该线程成功获取了轻量级锁;如果替换失败,说明有其他线程正在竞争该锁,当前线程会尝试自旋等待锁的释放。

(2)升级条件:如果自旋次数达到一定阈值(通常由 JVM 动态调整),或者有多个线程同时竞争该锁,轻量级锁会升级为重量级锁。

(4)重量级锁

原理:重量级锁依赖于操作系统的互斥量(Mutex)来实现,当线程获取重量级锁失败时,会被阻塞,进入等待队列,直到锁被释放。重量级锁的性能开销较大,因为线程的阻塞和唤醒需要操作系统进行上下文切换,这会消耗较多的 CPU 时间。

【3】总结

synchronized 锁的实现原理基于对象头和监视器,通过 monitorenter 和 monitorexit 指令或 ACC_SYNCHRONIZED 标志来实现线程同步。锁升级原理是为了在不同的竞争情况下选择合适的锁状态,以提高性能。在无竞争的情况下,使用偏向锁;在有少量竞争的情况下,使用轻量级锁;在竞争激烈的情况下,使用重量级锁。

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

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

相关文章

大模型在原发性急性闭角型青光眼预测及治疗方案制定中的应用研究报告

目录 一、引言 1.1 研究背景与意义 1.2 研究目的与方法 1.3 国内外研究现状 二、原发性急性闭角型青光眼概述 2.1 疾病定义与分类 2.2 发病机制与危险因素 2.3 症状与诊断方法 三、大模型在原发性急性闭角型青光眼预测中的应用 3.1 大模型原理与优势 3.2 术前风险预…

【蓝桥杯—单片机】第十五届省赛真题代码题解析 | 思路整理

第十五届省赛真题代码题解析 前言赛题代码思路笔记竞赛板配置建立模板明确基本要求显示功能部分频率界面正常显示高位熄灭 参数界面基础写法:两个界面分开来写优化写法:两个界面合一起写 时间界面回显界面校准校准过程校准错误显示 DAC输出部分按键功能部…

Vue3实战学习(Vue3快速搭建后台管理系统(网页头部、侧边导航栏、主体数据展示区的设计与实现)(超详细))(9)

目录 一、Vue3工程环境配置、项目基础脚手架搭建、Vue3基础语法、Vue3集成Element-Plus的详细教程。(博客链接如下) 二、Vue3集成Element-Plus详细教程。(博客链接如下) 三、Vue3集成Vue-Router详细教程。(博客链接如下) 四、Vue3快速搭建后台管理系统。(实战学习) &#xff08…

halcon机器人视觉(四)calibrate_hand_eye_stationary_3d_sensor

目录 一、准备数据和模型二、按照表面匹配的的结果进行手眼标定三、根据标定结果计算CalObjInCamPose一、准备数据和模型 1、读3D模型:read_object_model_3d 2、创建表面匹配模板:create_surface_model 3、创建一个HALCON校准数据模型:create_calib_data read_object_mode…

【菜鸟飞】通过vsCode用python访问deepseek-r1等模型

目标 通过vsCode用python访问deepseek。 环境准备 没有环境的,vscode环境准备请参考之前的文章,另外需安装ollama: 【菜鸟飞】用vsCode搭建python运行环境-CSDN博客 AI入门1:AI模型管家婆ollama的安装和使用-CSDN博客 选读文章…

vue中,watch里,this为undefined的两种解决办法

提示:vue中,watch里,this为undefined的两种解决办法 文章目录 [TOC](文章目录) 前言一、问题二、方法1——使用function函数代替箭头函数()>{}三、方法2——使用that总结 前言 ‌‌‌‌‌尽量使用方法1——使用function函数代替箭头函数()…

【如何使用云服务器与API搭建专属聊天系统:宝塔面板 + Openwebui 完整教程】

文章目录 不挑电脑、不用技术,云服务器 API 轻松搭建专属聊天系统,对接 200 模型,数据全在自己服务器,安全超高一、前置准备:3 分钟快速上手指南云服务器准备相关账号注册 二、手把手部署教程(含代码块&a…

使用 PresentMon 获取屏幕帧率

PresentMon是一个用于捕获和分析Windows上图形应用程序高性能特性的工具集,最初由GameTechDev开发,现由英特尔维护和推广。PresentMon能够追踪关键性能指标,如CPU、GPU和显示器的帧持续时间和延迟等,并支持多种图形API(如DirectX、OpenGL和Vulkan)以及不同的硬件配置和桌…

基金交易系统的流程

1. 账户管理 开户:投资者在基金销售机构(如银行、券商或第三方平台)开立基金账户和资金账户。账户验证:验证投资者身份,绑定银行卡或其他支付方式。 2. 交易申请 投资者下单:通过交易终端(APP…

vue2双向绑定解析

Vue 2 的双向绑定原理基于 Object.defineProperty,核心源码在 src/core/observer 目录中。 1. 核心模块:observer observer 模块负责将普通对象转换为响应式对象,主要包括以下文件: index.js:定义 Observer 类&#…

中级软件设计师2004-2024软考真题合集下载

中级软件设计师2004-2024软考真题合集下载 🌟 资源亮点🎯 适用人群💡 资源使用指南📌 资源获取方式 🌟 资源亮点 「中级软件设计师历年真题及答案解析(2004-2024)」 是全网最全、最新的备考资料…

艾尔登复刻Ep1——客户端制作、场景切换、网络控制

需要添加的插件内容 Netcode for GameObjects:是一个为 Unity 游戏开发提供高级网络功能的 SDK。它的主要作用是允许开发者在其 GameObject 和 MonoBehaviour 工作流中集成网络功能,并且可以与多种底层传输层协议兼容。 具体内容请看:https:…

2025探索短剧行业新可能报告40+份汇总解读|附PDF下载

原文链接:https://tecdat.cn/?p41043 近年来,短剧以其紧凑的剧情、碎片化的观看体验,迅速吸引了大量用户。百度作为互联网巨头,在短剧领域积极布局。从早期建立行业专属模型冷启动,到如今构建完整的商业生态&#xf…

正常的一个编码器的架构是怎么样,有哪些模块构成

正常的一个编码器架构及其模块构成 在音视频编码器(Video Encoder)中,通常包括多个核心模块,整个编码器架构遵循 输入 -> 预处理 -> 编码核心 -> 码流封装 的流程。 1. 编码器的整体架构 编码器的主要架构如下&#xf…

文件解析漏洞练习

iis6的目录解析漏洞 (.asp目录中的所有文件都会被当做asp文件执行) 1.在iis的网站根目录新建一个名为x.asp的文件 2.在x.asp中新建一个jpg文件。内容为<%now()%> asp代码。 3.在外部浏览器中访问windows2003的iis网站中的2.jpg 发现asp代码被执行 iis6的分号截断解析漏洞…

SQL Server性能优化实战:从瓶颈定位到高效调优

引言 在数据库应用中,性能问题直接影响用户体验和系统稳定性。本文基于实际案例,分享SQL Server性能优化的关键步骤与实用技巧,涵盖问题定位、索引优化、查询调优等多个维度。 目录 引言 一、性能瓶颈定位 1.1 监控工具使用 二、索引优化实战 2.1 索引碎片整理 2.2 缺…

【DNS系列】使用TCP传输

DNS ​默认使用UDP协议​&#xff08;端口53&#xff09;进行通信&#xff0c;但在以下场景中会切换到TCP协议​&#xff08;端口53&#xff09;&#xff1a; ​1. 响应数据过大&#xff08;超过512字节&#xff09;​ ​UDP限制&#xff1a;DNS的UDP协议默认限制单个数据包大…

Go Ebiten小游戏开发:俄罗斯方块

在这篇文章中&#xff0c;我们将一起开发一个简单的俄罗斯方块游戏&#xff0c;使用Go语言和Ebiten游戏库。Ebiten是一个轻量级的游戏库&#xff0c;适合快速开发2D游戏。我们将逐步构建游戏的基本功能&#xff0c;包括游戏逻辑、图形绘制和用户输入处理。 项目结构 我们的项…

MySQL中IN关键字与EXIST关键字的比较

文章目录 **功能等价性分析****执行计划分析**&#xff1a; **1. EXISTS 的工作原理****步骤拆解**&#xff1a; **2. 为什么需要“利用索引快速定位”&#xff1f;****索引作用示例**&#xff1a; **3. 与 IN 子查询的对比****IN 的工作方式**&#xff1a;**关键差异**&#x…

## DeepSeek写水果记忆配对手机小游戏

DeepSeek写水果记忆配对手机小游戏 提问 根据提的要求&#xff0c;让DeepSeek整理的需求&#xff0c;进行提问&#xff0c;内容如下&#xff1a; 请生成一个包含以下功能的可运行移动端水果记忆配对小游戏H5文件&#xff1a; 要求 可以重新开始游戏 可以暂停游戏 卡片里的水果…