线程池(二):深入剖析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,_ownernull
  • 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同步代码块时,如果发现是重量级锁,它会进入ObjectMonitorEntryList队列中等待。持有锁的线程执行完同步代码后,会释放锁,然后从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来解决多线程同步问题。

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

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

相关文章

Linux CentOS 7 安装Apache 部署html页面

*、使用yum包管理器安装Apache。运行以下命令: sudo yum install httpd *、启动Apache服务 sudo systemctl start httpd *、设置Apache服务开机自启 # 启用开机自启动 sudo systemctl enable httpd# 禁用开机自启动 sudo systemctl disable httpd *、验证Apac…

前端设置三行文本省略号,失效为什么?

实际效果:第三行出现省略号,但是第四行依旧显示了部分文字 这个问题通常是由于 CSS 多行文本截断(-webkit-line-clamp)的计算方式或布局冲突导致的。以下是完整解决方案,确保三行文本截断正确显示省略号,并…

git学习之git常用命令

1. 初始化仓库 git init初始化一个新的 Git 仓库。 2. 克隆远程仓库 git clone <repository-url>从远程服务器克隆一个已有仓库到本地。 3. 配置用户名和邮箱 git config --global user.name "Your Name" git config --global user.email "youexampl…

【Spring Boot】深入解析:#{} 和 ${}

1.#{} 和 ${}的使用 1.1数据准备 1.1.1.MySQL数据准备 &#xff08;1&#xff09;创建数据库&#xff1a; CREATE DATABASE mybatis_study DEFAULT CHARACTER SET utf8mb4;&#xff08;2&#xff09;使用数据库 -- 使⽤数据数据 USE mybatis_study;&#xff08;3&#xff…

Poco C++全面开发指南:日期和时间

时间戳 时间戳是指格林威治时间1970年01月01日00时00分00秒&#xff08;北京时间1970年01月01日08时00分00秒&#xff09;起至现在的总秒数。在poco中可以可以使用Timestamp类获取。 #include <Poco/Timestamp.h> #include <iostream>int main() {Poco::Timestam…

水利三维可视化平台怎么做?快速上手的3步指南

分享大纲&#xff1a; 1、了解水利三维可视化平台 2、选择合适的开发平台 3、快速搭建水利三维可视化平台 第一步&#xff1a;了解水利三维可视化平台 水利三维可视化平台是利用大数据、物联网、数字孪生等技术&#xff0c;将物理实体数字化建模&#xff0c;并通过三维可视化技…

高级前端面试题:基于2025年最新技术体系

高级前端面试题:基于2025年最新技术体系 引言 随着前端技术的不断发展,2025年的前端面试题也呈现出新的特点和趋势。本报告基于最新的前端技术体系,收集了当前热门的面试题,旨在帮助准备高级前端工程师面试的候选人全面了解面试考察点。报告内容涵盖HTML5 Canvas、WebGL、…

图像处理——边缘检测

1 概述 边缘检测是图像处理和计算机视觉中的一项基本技术&#xff0c;用于识别图像中亮度变化剧烈的像素点&#xff0c;这些像素点通常对应于物体的边界。它通过检测图像中亮度或颜色变化显著的区域&#xff0c;提取出物体的轮廓&#xff0c;常用于计算机视觉、图像处理和模式识…

c语言的常用的预处理指令和条件编译

c语言的常用的预处理指令和条件编译 预处理详解预定义符号#define#define 定义标识符#define 定义宏带副作用的宏参数宏和函数的对比#define命名约定和#undef移除宏 # 和 ## 参数插入字符串字符串的自动连接#宏参数 命令行定义条件编译#if和#endif多分支条件编译#if、#elif、#e…

TTL、RS-232 和 RS-485 串行通信电平标准区别解析

TTL、RS-232 和 RS-485 是三种常见的串行通信电平标准&#xff0c;它们各自有不同的协议特点&#xff0c;适用于不同的应用场景。以下是它们的主要特点对比&#xff1a; ​​1. TTL&#xff08;Transistor-Transistor Logic&#xff09;​​ ​​主要特点​​ ​​单端信号​…

SwinTransformer改进(6):与Dual Cross-Attention结合的视觉模型

在计算机视觉领域,Transformer架构正逐渐取代传统的CNN成为主流。 本文将深入解析一个结合了Swin Transformer和Dual Cross-Attention(DCA)的创新模型实现。 模型概述 这个实现的核心是将Swin Transformer(一种高效的视觉Transformer)与创新的Dual Cross-Attention模块相结…

Dify框架面试内容整理-Dify框架

什么是Dify框架? Dify框架是一个开源的AI应用开发平台,专注于帮助开发者和非技术人员快速构建、部署和管理基于大语言模型(如GPT系列、国产开源模型)的应用。 Dify框架的特点:

道可云人工智能每日资讯|“人工智能科技体验展”在中国科学技术馆举行

道可云元宇宙每日简报&#xff08;2025年4月28日&#xff09;讯&#xff0c;今日元宇宙新鲜事有&#xff1a; 《2025年提升全民数字素养与技能工作要点》发布 近日&#xff0c;中央网信办、教育部、工业和信息化部、人力资源社会保障部联合印发《2025年提升全民数字素养与技能…

基于javaweb的SpringBoot新闻发布系统设计与实现(源码+文档+部署讲解)

技术范围&#xff1a;SpringBoot、Vue、SSM、HLMT、Jsp、PHP、Nodejs、Python、爬虫、数据可视化、小程序、安卓app、大数据、物联网、机器学习等设计与开发。 主要内容&#xff1a;免费功能设计、开题报告、任务书、中期检查PPT、系统功能实现、代码编写、论文编写和辅导、论文…

苍穹外卖心得体会

1 登录认证 技术点&#xff1a;JWT令牌技术&#xff08;JSON Web Token&#xff09; JWT&#xff08;JSON Web Token&#xff09;是一种令牌技术&#xff0c;主要由三部分组成&#xff1a;Header头部、Payload载荷和Signature签名。Header头部存储令牌的类型&#xff08;如JW…

车载功能测试-车载域控/BCM控制器测试用例开发流程【用例导出方法+优先级划分原则】

目录 1 摘要2 位置灯手动控制简述2.1 位置灯手动控制需求简述2.2 位置灯手动控制逻辑交互图 3 用例导出方法以及优先级原则3.1 用例导出方法3.1.1 用例导出方法介绍3.1.2 用例导出方法关键差异分析 3.2 优先级规则3.2.1 优先级划分的核心原则3.2.2 具体等级定义与判定标准 3.3 …

Linux系统基础:基础指令简介(网络概念部分)

简介&#xff1a;Linux 是一种开源的类 Unix 操作系统内核&#xff0c;由 Linus Torvalds 于 1991 年首次发布。经过多年发展&#xff0c;它已成为服务器、嵌入式设备和个人计算机领域的重要操作系统。 网络基础概念 初始协议 简单来说&#xff0c;协议是一种约定&#xff0…

多模态(3):实战 GPT-4o 视频理解

最近&#xff0c;OpenAI 团队的 GPT-4o 模型&#xff0c;在多模态方面的能力有了大幅提升&#xff0c;这次我们就使用 GPT-4o 完成一个视频理解的实战。 1. 环境搭建 1.1 安装 FFmpeg 做视频处理&#xff0c;我们需要用到 FFmpeg 这款功能强大的开源多媒体处理工具。FFmpeg…

(27)VTK C++开发示例 ---将点坐标写入 STL文件

文章目录 1. 概述2. CMake链接VTK3. main.cpp文件4. 演示效果 更多精彩内容&#x1f449;内容导航 &#x1f448;&#x1f449;VTK开发 &#x1f448; 1. 概述 此示例使用 vtkSTLWriter 将存储在 vtkPolyData 对象中的 3D 几何数据保存到 STL 文件&#xff0c;并读取stl文件显示…

2. python协程/异步编程详解

目录 1. 简单的异步程序 2. 协程函数和协程对象 3. 事件循环 4. 任务对象Task及Future对象 4.1 Task与Future的关系 4.2 Future对象 4.3 全局对象和循环事件对象 5. await关键字 6. 异步上下文管理 7.异步迭代器 8. asyncio的常用函数 8.1 asyncio.run 8.2 asyncio.get…