线程池(二):深入剖析synchronized关键字的底层原理
线程池(二):深入剖析synchronized关键字的底层原理
- 线程池(二):深入剖析`synchronized`关键字的底层原理
- 一、基本使用
- 1.1 修饰实例方法
- 1.2 修饰静态方法
- 1.3 修饰代码块
- 二、Monitor
- 2.1 Monitor的概念
- 2.2 Monitor的实现原理
- 2.3 Monitor与`synchronized`的关系
- 三、synchronized关键字的底层原理 - 进阶
- 3.1 对象的内存结构
- 3.2 MarkWord
- 3.3 再说Monitor重量级锁
- 3.4 轻量级锁
- 3.5 偏向锁
- 3.6 谈谈JMM(Java内存模型)
线程池(二):深入剖析synchronized
关键字的底层原理
一、基本使用
1.1 修饰实例方法
当synchronized
修饰一个实例方法时,它锁定的是当前对象(this
)。例如:
public class SynchronizedExample {public synchronized void synchronizedMethod() {// 同步代码块// 同一时刻,只有一个线程能进入这个方法}
}
在上述代码中,任何线程在调用synchronizedMethod
方法时,都需要获取当前对象的锁。如果一个线程已经持有了这个锁,其他线程就需要等待,直到该线程释放锁。
1.2 修饰静态方法
当synchronized
修饰静态方法时,它锁定的是当前类的Class
对象。因为静态方法属于类,而不是某个具体的实例。示例如下:
public class StaticSynchronizedExample {public static synchronized void staticSynchronizedMethod() {// 同步代码块// 同一时刻,只有一个线程能进入这个静态方法}
}
不管有多少个该类的实例,对于这个静态同步方法,同一时刻只有一个线程可以执行。这是因为所有线程共享类的Class
对象,锁的就是这个唯一的Class
对象。
1.3 修饰代码块
synchronized
还可以修饰代码块,这种方式更加灵活,可以指定具体要锁定的对象。例如:
public class SynchronizedBlockExample {private final Object lock = new Object();public void someMethod() {synchronized (lock) {// 同步代码块// 同一时刻,只有一个线程能进入这个代码块}}
}
这里通过synchronized (lock)
指定了锁定的对象是lock
。当多个线程同时访问someMethod
方法时,只有一个线程能获取到lock
对象的锁并执行同步代码块中的内容。
二、Monitor
2.1 Monitor的概念
Monitor(监视器)是Java并发编程中实现同步的一个核心概念。它可以理解为一个同步工具,也可以说是一种同步机制。每个Java对象都可以关联一个Monitor。当一个线程想要进入同步代码块(无论是synchronized
修饰的方法还是代码块)时,它需要先获取对应的Monitor。
2.2 Monitor的实现原理
在HotSpot虚拟机中,Monitor是由ObjectMonitor
结构体实现的。它主要包含以下几个关键部分:
- header:对象头,用于存储对象的一些元数据信息,比如对象的哈希码、对象的分代年龄等。
- count:记录该Monitor被获取的次数。当一个线程成功获取到Monitor后,
_count
会加1,每次释放锁时,_count
会减1。当_count
为0时,代表该Monitor没有被任何线程持有。 - owner:指向当前持有该Monitor的线程。如果当前没有线程持有该Monitor,
_owner
为null
。 - WaitSet:等待队列,当线程调用对象的
wait()
方法时,该线程会被放入这个等待队列中,进入等待状态。 - EntryList:入口队列,当多个线程同时竞争一个Monitor时,没有获取到锁的线程会被放入这个入口队列中等待。
2.3 Monitor与synchronized
的关系
synchronized
关键字的底层实现依赖于Monitor。当一个线程进入synchronized
修饰的同步代码块或方法时,实际上就是去获取对应的Monitor。如果获取成功,就可以执行同步代码;如果获取失败,就会被放入EntryList
队列中等待。当持有锁的线程执行完同步代码或者调用wait()
方法时,会释放Monitor,此时会从EntryList
队列中唤醒一个等待的线程来获取Monitor。
三、synchronized关键字的底层原理 - 进阶
3.1 对象的内存结构
在Java中,对象在内存中的布局主要包括三个部分:对象头(Header)、实例数据(Instance Data)和对齐填充(Padding)。
- 对象头(Header):对象头又分为两部分,一部分是用于存储对象自身的运行时数据,比如哈希码(HashCode)、对象的分代年龄、锁标志位等,这部分数据被称为
MarkWord
;另一部分是指向对象所属类的Class
对象的指针,用于确定对象的类型。 - 实例数据(Instance Data):这部分用于存储对象的成员变量,包括从父类继承下来的成员变量和本类定义的成员变量。
- 对齐填充(Padding):由于虚拟机要求对象的起始地址必须是8字节的整数倍,所以当对象头和实例数据部分的总大小不是8字节的整数倍时,需要通过对齐填充来补足。
3.2 MarkWord
MarkWord
是对象头中非常重要的一部分,它在不同的锁状态下会存储不同的信息。在32位虚拟机中,MarkWord
的长度是32位(4个字节),在64位虚拟机中,MarkWord
的长度是64位(8个字节)。以下是不同锁状态下MarkWord
的存储内容:
- 无锁状态:在无锁状态下,
MarkWord
存储对象的哈希码、对象的分代年龄等信息。例如在32位虚拟机中,前25位存储对象的哈希码,后4位存储对象的分代年龄,最后3位是锁标志位(01表示无锁)。 - 偏向锁状态:当对象进入偏向锁状态时,
MarkWord
中会存储持有该锁的线程ID等信息。 - 轻量级锁状态:在轻量级锁状态下,
MarkWord
会存储指向栈帧中锁记录的指针。 - 重量级锁状态:当对象处于重量级锁状态时,
MarkWord
会存储指向Monitor对象的指针。
3.3 再说Monitor重量级锁
当多个线程竞争同一个锁,且竞争比较激烈时,轻量级锁会升级为重量级锁。此时,MarkWord
中存储的是指向ObjectMonitor
的指针。重量级锁是通过操作系统的互斥量(Mutex)来实现的,线程获取和释放锁都需要进行用户态和内核态的切换,这种切换开销比较大。
当一个线程进入synchronized
同步代码块时,如果发现是重量级锁,它会进入ObjectMonitor
的EntryList
队列中等待。持有锁的线程执行完同步代码后,会释放锁,然后从EntryList
队列中唤醒一个等待的线程。被唤醒的线程会再次尝试获取锁,获取成功后才能执行同步代码。
3.4 轻量级锁
轻量级锁是为了在没有多线程竞争或者竞争不激烈的情况下,减少获取锁和释放锁的开销而引入的。当一个线程进入synchronized
同步代码块时,会在当前线程的栈帧中创建一个锁记录(Lock Record),并将MarkWord
复制到锁记录中。然后,线程尝试通过CAS(Compare and Swap,比较并交换)操作将MarkWord
更新为指向锁记录的指针。如果CAS操作成功,说明该线程获取到了轻量级锁,就可以执行同步代码。
如果CAS操作失败,说明有其他线程已经持有了该锁,此时轻量级锁会尝试自旋(Spin)一定次数来等待锁的释放。自旋是指线程不放弃CPU的执行权,在原地等待一段时间,希望持有锁的线程能尽快释放锁。如果自旋一定次数后仍然没有获取到锁,轻量级锁就会升级为重量级锁。
3.5 偏向锁
偏向锁是在JDK 6中引入的,它的目的是为了在只有一个线程访问同步代码块的情况下,进一步减少获取锁的开销。当一个线程访问synchronized
同步代码块时,会检查MarkWord
中是否已经记录了该线程的ID。如果已经记录,说明该线程已经持有了偏向锁,直接进入同步代码块执行。
如果MarkWord
中没有记录该线程的ID,会通过CAS操作将线程ID记录到MarkWord
中。如果CAS操作成功,就表示该线程获取到了偏向锁。当有其他线程尝试获取该锁时,偏向锁会被撤销,升级为轻量级锁。
3.6 谈谈JMM(Java内存模型)
Java内存模型(JMM)定义了Java程序中多线程访问共享变量的规则。它规定了一个线程如何和何时可以看到由其他线程修改过后的共享变量的值,以及在必须时如何同步的访问共享变量。
在synchronized
关键字的实现中,JMM起到了重要的作用。当一个线程获取到锁进入synchronized
同步代码块时,会从主内存中读取共享变量的值到工作内存中。在同步代码块执行过程中,对共享变量的修改会先在工作内存中进行。当线程执行完同步代码块释放锁时,会将工作内存中修改后的共享变量的值写回到主内存中。这样可以保证在同一时刻,只有一个线程能够对共享变量进行修改,并且其他线程能够看到最新的修改结果,从而保证了多线程环境下共享变量的可见性和一致性。
通过对synchronized
关键字从基本使用到深入底层原理的剖析,包括Monitor机制、对象内存结构、不同锁状态以及与Java内存模型的关系等方面,我们对synchronized
在Java并发编程中的作用和实现有了一个全面而深入的理解。这有助于我们在实际开发中更合理、高效地使用synchronized
来解决多线程同步问题。
相关文章:
线程池(二):深入剖析synchronized关键字的底层原理
线程池(二):深入剖析synchronized关键字的底层原理 线程池(二):深入剖析synchronized关键字的底层原理一、基本使用1.1 修饰实例方法1.2 修饰静态方法1.3 修饰代码块 二、Monitor2.1 Monitor的概念2.2 Moni…...
【线段树】P8539 「Wdoi-2」来自地上的支援|普及+
P8539 「Wdoi-2」来自地上的支援 题目背景 波光粼粼的山顶湖与庄严神圣的神社之下,是一座复合型活火山。 沿幻想风穴而下,便能到达火山之下,废弃已久的地狱原址。 在旧地狱中,有一座大都市。那里是旧地狱还是地狱的时候在那工作…...

《TCP/IP详解 卷1:协议》之第七、八章:Ping Traceroute
目录 一、ICMP回显请求和回显应答 1、ICMP回显请求 2、ICMP回显应答 二、ARP高速缓存 三、IP记录路由选项(Record Route,RR) 1、记录路由选项的工作过程 2、RR 选项的 IP 头部格式 2.1、RR 请求 2.2、RR响应 四、ping 的去返路径 五…...
Leetcode:1. 两数之和
题目 给定一个整数数组 nums 和一个整数目标值 target,请你在该数组中找出 和为目标值 target 的那 两个 整数,并返回它们的数组下标。 你可以假设每种输入只会对应一个答案,并且你不能使用两次相同的元素。 你可以按任意顺序返回答案。 示…...
【Java学习笔记】冒泡排序
冒泡排序 思想:经过一轮遍历比较,把最大的放在数组的末尾 int[] a {3, 2, 1}; for( int i 0; i < a.length-1; i){for( int j 0; j < a.length-1-i; j){if(a[j] > a[j1]){int temp a[j];a[j] a[j1];a[j1] temp;}} } for( int i 0; i &…...
【数字图像处理】立体视觉基础(2)
相机标定 【1】相机标定的概念 相机参数:相机成像的几何模型的参数 相机标定:求解参数的过程 【2】相机标定的作用 (1)求出相机的内、外参数,以及畸变参数 (2)校正镜头畸变影响,…...

NtripShare 2025第一季度主要技术进展
GNSS方面 1、开源GNSS接收机配置软件基础版本。 2、商业版本GNSS接收机配置软件,增加PPP、文件保存、前端解算(静态、RTK-Static),前端坐标转换。 3、GNSS接收机配置软件全面适配米尔T133i硬件方案。 视觉检测方面 1、做出第…...

头歌实训之存储过程、函数与触发器
🌟 各位看官好,我是maomi_9526! 🌍 种一棵树最好是十年前,其次是现在! 🚀 今天来学习C语言的相关知识。 👍 如果觉得这篇文章有帮助,欢迎您一键三连,分享给更…...

【华为】防火墙双击热备-之-主备模式-单外网线路-分享
FW1和FW2的业务接口都工作在三层,上行连接二层交换机。上行交换机连接运营商的接入点,运营商为企业分配的IP地址为100.100.100.2。现在希望FW1和FW2以主备备份方式工作。正常情况下,流量通过FW1转发;当FW1出现故障时,流…...
c++ package_task
int print_sum(int a, int b) {std::cout << a << " " << b << " " << (a b) << std::endl;return a b; }int main() {// 创建绑定后的可调用对象auto print_sum_5 std::bind(print_sum, 5, 6);// 包装为 packag…...
基于计算机视觉的行为检测:从原理到工业实践
一、行为检测的定义与核心价值 行为检测(Action Recognition)是计算机视觉领域的关键任务,旨在通过分析视频序列理解人类动作的时空特征。其核心价值体现在时序建模和多尺度分析能力上——系统需要捕捉动作的起始、发展和结束全过程,同时适应不同持续时间(0.1秒至数分钟)…...

川翔云电脑32G大显存集群机器上线!
川翔云电脑今日重磅推出32G 大显存机型,为游戏玩家、设计师、AI 开发者等提供极致云端算力体验! 一、两大核心配置,突破性能天花板 ✅ 32G 超大显存机型 行业领先:搭载 NVIDIA 专业显卡,单卡可分配 32G 独立显存&am…...

加里·基尔代尔:CP/M之父与个人计算时代的先驱
名人说:路漫漫其修远兮,吾将上下而求索。—— 屈原《离骚》 创作者:Code_流苏(CSDN)(一个喜欢古诗词和编程的Coder😊) 加里基尔代尔:CP/M之父与个人计算时代的先驱 一、早年生活与教育背景 1.…...

静态多态和动态多态的区别
C多态机制深度解析 多态是面向对象编程的核心特性,允许通过统一接口执行不同实现。在C中,多态表现为基类指针或引用调用虚函数时,根据实际对象类型执行对应派生类的函数逻辑。 基础实现示例 定义基类与派生类,演示动态绑定…...
MQL5教程 06 EA开发实战
文章目录 一、调用多指标多周期EA示例二、获取ZigZag顶点值三、逆势加仓EA 一、调用多指标多周期EA示例 shuju.mqh: class shuju{public:shuju(){} ~shuju(){}void MA(double &ma[],int count,string symbol, // 交易品种名称 ENUM_TIMEFRA…...
LLaMa Factory大模型微调
LLaMa Factory大模型微调 大模型微调平台&硬件LLaMA-Factory安装hfd下载hugging face模型自我认知微调Alpaca数据集指令监督微调断点续训 大模型微调 微调自我认知微调特定领域数据集。 平台&硬件 Ubuntu20.04显卡:M40 24G 2080TI 22G微调框架ÿ…...

Burp靶场JWT学习笔记1
JWT(JSON Web Token) 从其名字就可以看出来,它具有表示身份的作用,其本质是将用户信息储存到一串json字符串中再将其编码得到一串token JWT由三部分组成,分别是 Header,Payload,Signatrue JWTBase64(Header).Base6…...

C++?类和对象(下)!!!
一、前言 在之前我们已经讨论过了有关类和对象的前置知识以及类中的六大默认成员函数,在本期我们继续再讨论类和对象中剩余的友元、初始化列表等相关知识,如果需要再了解之前的知识的话,链接奉上:C?类和对象࿰…...

FastAPI 零基础入门指南:10 分钟搭建高性能 API
一、为什么选择 FastAPI? 想象一下,用 Python 写 API 可以像搭积木一样简单,同时还能拥有媲美 Go 语言的性能,这个框架凭借三大核心优势迅速风靡全球: 开发效率提升 3 倍:类型注解 自动文档,…...
prometheus通过Endpoints自定义grafana的dashboard模块
1、prometheus自定义的dashboard模块 文件路径/etc/prometheus/config_out/prometheus-env.yaml - job_name: serviceMonitor/monitoring/pfil/0honor_labels: falsekubernetes_sd_configs:- role: endpointsnamespaces:names:- monitoringrelabel_configs:- source_labels:- …...

机器人新革命:Pi 0.5如何让智能走进千家万户
在科技飞速发展的今天,机器人技术正在以一种令人惊喜的方式贴近我们的生活。最近,Physical Intelligence 公司推出了 Pi 0.5 版本,这一创新设计不仅颠覆了传统机器人的运作模式,更让我们看到了未来智能设备融入日常生活的无限可能…...
std::mutex底层实现原理
std::mutex是一个用于实现互斥访问的类,其具备两个成员函数——lock和unlock 锁的底层实现原理 锁的底层实现是基于原子操作的,这些原子操作是由指令支持的,因为单个指令是不能被中断的 一些与锁的实现有关的原子指令为: 待补充…...

从数据结构说起(一)
1 揭开数据结构神奇的面纱 1.1 初识数据结构 在C的标准库模板(Standard Template Library,STL)课程上,我初次结识了《数据结构》。C语言提供的标准库模板是面向对象程序设计与泛型程序设计思想相结合的典范。所谓的泛型编程就是编写不依赖于具…...
【后端】构建简洁的音频转写系统:基于火山引擎ASR实现
在当今数字化时代,语音识别技术已经成为许多应用不可或缺的一部分。无论是会议记录、语音助手还是内容字幕,将语音转化为文本的能力对提升用户体验和工作效率至关重要。本文将介绍如何构建一个简洁的音频转写系统,专注于文件上传、云存储以及…...
矫平机终极指南:特殊材料处理、工艺链协同与全球供应链管理
一、特殊材料矫平:挑战与创新解决方案 1. 高温合金(如Inconel 718)处理 技术难点: 屈服强度高达1100 MPa,传统矫平力不足 高温下易氧化,需惰性气体保护环境 解决方案: 采用双伺服电机驱动&a…...
云服务器 —— 公有 IP 与 私有 IP
云服务器的 公有 IP 和 私有 IP 在网络架构中扮演不同的角色,具体用途和区别如下: 目录 1. 公有 IP(Public IP) 作用: 特点: 示例场景: 2. 私有 IP(Private IP) 作用…...

Git基本使用(很详细)
一:Git 概述 1.1 定义:分布式版本控制系统 1.2 版本控制 (1)定义: 版本控制时一种记录文件内容变化,以便将来查阅特定版本修订情况的系统 (2)举例 多副本 优化: 不使用多…...
【人工智能】基于Python和Transformers库构建高效问答系统的实践与实现**
《Python OpenCV从菜鸟到高手》带你进入图像处理与计算机视觉的大门! 解锁Python编程的无限可能:《奇妙的Python》带你漫游代码世界 随着自然语言处理(NLP)的发展,问答系统成为了人工智能应用中的一个重要领域。近年来,预训练模型如BERT、GPT、T5等,通过大规模数据的预…...

仓颉编程语言最佳实例 “Hello, world!”
仓颉编程语言最佳实例 “Hello, world!” The Best Practice to Cangjie Programming Language - “Hello, world!” BY JACKSON 1. 仓颉集成开发工具(IDE)安装 打开Chrome浏览器,访问仓颉编程语言官网:https://cangjie-lang.…...
【机器学习-线性回归-3】深入浅出:简单线性回归的概念、原理与实现
在机器学习的世界里,线性回归是最基础也是最常用的算法之一。作为预测分析的基石,简单线性回归为我们理解更复杂的模型提供了完美的起点。无论你是机器学习的新手还是希望巩固基础的老手,理解简单线性回归都至关重要。本文将带你全面了解简单…...