并发编程-Synchronized

Mark Word

什么是Mark Word

Mark Word是Java对象头中的一个字段,它是一个32位或64位的字段(取决于系统架构),用于存储对象的元数据信息。这些信息包括对象的哈希码、锁状态、年龄等。

Mark Word有什么用?

Mark Word的主要用途包括:

  1. 存储哈希码:在对象需要计算哈希码时,可以直接从Mark Word中读取,避免重复计算。

  2. 管理锁状态Mark Word用于存储对象的锁状态,支持多种锁机制,如偏向锁、轻量级锁和重量级锁。

  3. 垃圾回收Mark Word中的年龄信息用于垃圾回收,帮助垃圾回收器决定何时回收对象。

Mark Word储存在哪?

下图为图示

Mark Word是Java对象头的一部分,存储在对象的内存布局中。对象的内存布局通常包括以下部分:

  1. Mark Word:存储对象的元数据信息。

  2. Klass Word:存储对象的类信息。

  3. 对象数据:存储对象的实际数据。

在32位系统中,Mark Word是一个32位的字段;在64位系统中,Mark Word是一个64位的字段。为了节省内存,Java在64位系统中使用了一种称为“指针压缩”的技术,将Mark Word压缩为32位。

结构

1. Normal(正常状态)

  • 位布局

    • hashcode:25:25位用于存储对象的哈希码。

    • age:4:4位用于存储对象的年龄(用于垃圾回收)。

    • biased_lock:0:1位用于表示是否启用偏向锁(0表示未启用)。

    • 01:2位用于表示锁的状态(01表示正常状态)。

2. Biased(偏向锁状态)

  • 位布局

    • thread:23:23位用于存储偏向锁的线程ID。

    • epoch:2:2位用于存储偏向锁的纪元信息。

    • age:4:4位用于存储对象的年龄。

    • biased_lock:1:1位用于表示是否启用偏向锁(1表示已启用)。

    • 01:2位用于表示锁的状态(01表示偏向锁状态)。

3. Lightweight Locked(轻量级锁状态)

  • 位布局

    • ptr_to_lock_record:30:30位用于存储指向锁记录的指针。

    • 00:2位用于表示锁的状态(00表示轻量级锁状态)。

4. Heavyweight Locked(重量级锁状态)

  • 位布局

    • ptr_to_heavyweight_monitor:30:30位用于存储指向重量级监视器的指针。

    • 10:2位用于表示锁的状态(10表示重量级锁状态)。

5. Marked for GC(标记为垃圾回收状态)

  • 位布局

    • 11:2位用于表示锁的状态(11表示标记为垃圾回收状态)。

Monitor

Monitor 是什么?

在 Java 中,Monitor(也称为 对象锁内置锁)是 JVM 实现线程同步的核心机制。它用于控制对共享资源的访问,确保在多线程环境下,对共享资源的访问是线程安全的。

一个对象会关联一个monitor

Monitor 的作用

Monitor 的主要作用是:

  • 实现线程同步:通过控制对共享资源的访问,确保同一时间只有一个线程可以访问共享资源。

  • 实现锁机制:Monitor 是 Java 中 synchronized 关键字的底层实现

  • 管理线程等待与唤醒:Monitor 内部维护了多个队列,用于管理等待锁的线程和等待被通知的线程。

  • JVM 统一管理 Monitor:虽然 Monitor 是对象的一部分,但 JVM 会通过全局的机制来管理所有 Monitor 的生命周期。例如,JVM 会维护一个全局的 Monitor 列表,用于分配和回收 Monitor 对象。

  • Monitor 的分配是线程安全的:JVM 会为每个线程维护一个可用的 Monitor 列表(freeused),当线程需要获取锁时,会从这些列表中申请 Monitor 对象。

Monitor 的结构

Monitor 是一个线程私有的数据结构,通常由 JVM 实现(如 HotSpot 虚拟机中的 ObjectMonitor 类)。它包含以下关键部分:

字段说明
_owner指向当前持有锁的线程的唯一标识(如 Thread 对象)
_EntryList等待获取锁的线程(阻塞队列)
_WaitSet等待被通知的线程(等待队列)
_recursions记录锁的重入次数
_count锁的计数器(用于判断是否为可重入锁)

Monitor 的生命周期

  • 创建:当一个对象被 synchronized 加锁时,JVM 会为该对象创建一个 Monitor 对象。

  • 销毁:Monitor 是与对象的生命周期一致的,当对象被垃圾回收时,Monitor 也会被销毁。

  • 统一管理:JVM 会通过全局机制来管理所有 Monitor 的生命周期,包括分配和回收。

总结

问题回答
Monitor 是什么?Monitor 是 Java 中实现线程同步的核心机制,是 synchronized 关键字的底层实现。
Monitor 的作用是什么?Monitor 的作用是实现线程同步,确保对共享资源的访问是线程安全的。
Monitor 的结构是什么?Monitor 包含 _owner_EntryList_WaitSet_recursions 等字段。
Monitor 的存储位置在哪里?Monitor 的地址存储在对象头的 Mark Word 中。
Monitor 是如何被创建和管理的?Monitor 是 JVM 自动创建的,与对象的生命周期一致,由 JVM 统一管理。
Monitor 的作用机制是什么?Monitor 通过 monitorentermonitorexit 实现锁的获取和释放,支持可重入性。
Monitor 的优化机制有哪些?包括偏向锁、轻量级锁和重量级锁等优化机制。

synchronized

是什么

synchronized 实际是用对象锁保证了临界区内代码的原子性,临界区内的代码对外是不可分割的,不会被线程切换所打断。

就是说 假设现在有两个线程,那么当线程1获取锁在执行的时候,当线程1的时间片用完,但是线程1里面的代码还没有执行完, 那么线程2就不能拿到锁,就会进入等待状态,当线程1执行完临界区代码,释放锁,线程2就会获取锁,继续执行临界区代码。


 

轻量级锁

Mark work布局

  • ptr_to_lock_record:30:30位用于存储指向锁记录的指针。

  • 00:2位用于表示锁的状态(00表示轻量级锁状态)。

轻量级锁是Java中一种用于优化同步操作的锁机制,其主要目的是在没有多线程竞争的情况下,减少锁的开销。轻量级锁的使用场景是当多个线程对同一个对象加锁的时间是错开的(即没有竞争)时,可以使用轻量级锁来优化。这种情况下,线程之间不会发生阻塞,从而提高了性能。

轻量级锁的加锁过程

当一个线程进入同步块时,如果该对象没有被锁定(即锁标志位为“01”状态),虚拟机会在当前线程的栈帧中创建一个名为锁记录(Lock Record)的空间,用于存储对象头中的Mark Word的拷贝。然后,虚拟机会尝试使用CAS操作将对象的Mark Word更新为指向锁记录的指针。

如果更新成功,表示当前线程获得了锁,并且对象的Mark Word的锁标志位会被设置为“00”,表示该对象处于轻量级锁状态。

轻量级锁的解锁过程

当线程退出同步块时,虚拟机会尝试使用CAS操作将当前线程的锁记录替换回对象头。如果替换成功,表示没有竞争发生,同步过程完成;如果替换失败,说明有其他线程尝试获取锁,此时需要唤醒被阻塞的线程。

加锁失败

在轻量级锁的加锁过程中,加锁失败通常有两种情况,而锁重入是其中一种特殊情况。以下是详细分析:

1. 锁已经被其他线程持有(竞争发生)

这是最常见的加锁失败原因。当一个线程在尝试获取轻量级锁时,发现对象的Mark Word已经被其他线程修改为“00”状态(表示轻量级锁状态),说明该对象已经被其他线程加锁,或者当前线程已经尝试过加锁但失败。

  • 原因:多个线程对同一个对象进行同步操作,且加锁时间重叠,导致竞争。

  • 处理方式:轻量级锁会尝试升级为重量级锁,通过操作系统级的互斥量来实现真正的互斥。

2. 锁重入(Reentrant Lock)

锁重入是轻量级锁的一种特殊情况,指的是同一个线程对同一个对象多次加锁。在轻量级锁中,每次重入时,锁记录中会存储一个重入计数器,表示该线程对锁的持有次数。

  • 判断方式:在加锁时,如果发现对象的Mark Word指向的是当前线程的栈帧,说明是锁重入,直接继续执行同步代码即可。

  • 处理方式:在锁重入时,锁记录中的displaced_header会被置为NULL,表示这是一个重入的锁记录。解锁时,如果发现displaced_headerNULL,则说明是锁重入,不需要恢复Mark Word,只需递减重入计数器即可。

重量级锁

Mark Work布局

  • ptr_to_heavyweight_monitor:30:30位用于存储指向重量级监视器的指针。

  • 10:2位用于表示锁的状态(10表示重量级锁状态)。

在多线程环境下,Java中的锁机制会根据竞争情况动态调整锁的状态,从无锁到偏向锁、轻量级锁,最终到重量级锁。当多个线程竞争同一个对象锁时,轻量级锁可能会膨胀为重量级锁。以下是详细的锁膨胀过程:

初始状态

  • 轻量级锁:当一个线程尝试获取对象锁时,如果对象处于无锁状态,线程会在自己的栈帧中创建一个锁记录(Lock Record),并将对象的Mark Word复制到这个锁记录中。然后,线程尝试通过CAS操作将对象的Mark Word更新为指向该锁记录的指针,并将Mark Word的最后两位设置为00,表示轻量级锁状态。

锁膨胀触发条件

  • CAS操作失败:如果在尝试加轻量级锁的过程中,CAS操作无法成功,说明有其他线程已经为该对象加了轻量级锁。此时,当前线程无法通过CAS操作将Mark Word更新为指向自己的锁记录,因此CAS操作会失败。

锁膨胀过程

  • 申请Monitor锁:当CAS操作失败后,线程会进入锁膨胀流程。首先,线程会为对象申请一个Monitor锁,并将对象的Mark Word更新为指向这个Monitor对象的地址。同时,Mark Word的最后两位会被设置为10,表示重量级锁状态。

  • 线程阻塞:由于重量级锁支持阻塞机制,当前线程会被放入Monitor的EntryList中,并进入BLOCKED状态,等待其他线程释放锁。

解锁过程

  • 解锁失败:当持有轻量级锁的线程(Thread-0)退出同步代码块时,它会尝试通过CAS操作将Mark Word恢复为原来的值(即之前保存在锁记录中的值)。然而,由于锁已经膨胀为重量级锁,Mark Word的值已经指向Monitor对象,并且最后两位为10,因此CAS操作会失败。

  • 重量级解锁流程:当CAS操作失败后,线程会进入重量级解锁流程。具体步骤如下:

    • 根据Mark Word中的Monitor地址找到对应的Monitor对象。

    • 将Monitor对象的Owner字段设置为null,表示没有线程持有该锁。

    • 唤醒EntryList中所有被阻塞的线程,让它们有机会竞争锁。

自旋优化 自适应自旋锁

重量级锁竞争的时候,还可以使用自旋来进行优化,如果当前线程自旋成功(即这时候持锁线程已经退出了同步块,释放了锁),这时当前线程就可以避免阻塞

自旋重试成功的情况

线程 1 (cpu 1 上)对象 Mark线程 2 (cpu 2 上)
-10(重量锁)-
访问同步块,获取 monitor10(重量锁)重量锁指针-
成功(加锁)10(重量锁)重量锁指针-
执行同步块10(重量锁)重量锁指针访问同步块,获取 monitor
执行同步块10(重量锁)重量锁指针自旋重试
执行完毕10(重量锁)重量锁指针自旋重试
成功(解锁)01(无锁)自旋重试
-10(重量锁)重量锁指针成功(加锁)
-10(重量锁)重量锁指针执行同步块

自旋锁一般会重试 10 次。这是 Java 中自旋锁的默认设置,由 JVM 内部参数 _spinFreq 控制,表示线程在尝试获取锁失败后,最多会自旋(即循环尝试)10 次。如果在这 10 次尝试中仍未成功获取锁,线程将不再自旋,而是进入阻塞状态,等待锁被释放 。

从 JDK 1.7 开始,自旋锁启用,并且自旋次数由 JVM 动态决定,称为“自适应自旋锁”。这种机制会根据前一次在同一个锁上的自旋时间和锁的持有者状态来调整自旋次数,从而在不同场景下都能达到最佳性能 。

自旋重试失败的情况

线程 1 (cpu 1 上)对象 Mark线程 2 (cpu 2 上)
-10(重量锁)-
访问同步块,获取 monitor10(重量锁)重量锁指针-
成功(加锁)10(重量锁)重量锁指针-
执行同步块10(重量锁)重量锁指针访问同步块,获取 monitor
执行同步块10(重量锁)重量锁指针自旋重试
执行同步块10(重量锁)重量锁指针自旋重试
执行同步块10(重量锁)重量锁指针阻塞
.........
  • 在 Java 6 之后自旋锁是自适应的,比如对象刚刚的一次自旋操作成功过,那么认为这次自旋成功的可能性会高,就多自旋几次;反之,就少自旋甚至不自旋,总之,比较智能。

  • 自旋会占用 CPU 时间,单核 CPU 自旋就是浪费,多核 CPU 自旋才能发挥优势。

  • Java 7 之后不能控制是否开启自旋功能

偏向锁

Mark Work布局

  • thread:23:23位用于存储偏向锁的线程ID。

  • epoch:2:2位用于存储偏向锁的纪元信息。

  • age:4:4位用于存储对象的年龄。

  • biased_lock:1:1位用于表示是否启用偏向锁(1表示已启用)。

  • 01:2位用于表示锁的状态(01表示偏向锁状态)。

偏向锁是Java 6中引入的一种锁优化机制,旨在进一步减少锁竞争带来的性能开销。它在无锁竞争的情况下,通过将锁直接偏向于第一个获取它的线程,从而避免了轻量级锁中频繁的CAS(Compare and Swap)操作。

偏向锁的引入背景

在轻量级锁的基础上,JVM发现大多数情况下锁并不会被多个线程竞争,尤其是同一个线程多次获取同一把锁的场景。为了进一步优化这种无竞争情况下的锁性能,JVM引入了偏向锁。偏向锁的核心思想是:将锁的持有者(线程)直接记录在对象头中,后续访问该锁的线程无需再进行CAS操作,从而减少不必要的锁竞争开销。

偏向锁的工作原理

第一次加锁:当第一个线程访问同步代码块时,JVM会通过CAS操作将该线程的ID写入对象头的Mark Word中,并将锁标志位设置为“101”(表示偏向锁状态)。

后续访问:当同一个线程再次访问该同步代码块时,JVM只需检查对象头中的线程ID是否与当前线程一致。如果一致,则直接进入同步代码块,无需再进行CAS操作。

锁重入:如果同一个线程对同一对象多次加锁,JVM会自动处理锁的重入,但每次重入时仍然只需判断线程ID是否匹配,无需执行CAS操作。

轻量级锁和偏向锁图示:

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

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

相关文章

【51单片机】5. 矩阵键盘与矩阵键盘密码锁Demo

1. 矩阵键盘原理 通过矩阵连接的模式,原本需要16个引脚连接的按钮只需要8个引脚就能连接好,减少了I/O口的占用。 矩阵按钮是通过扫描来读取状态的。 2. 扫描的概念 输出扫描示例:数码管扫描 原理:显示第1位→显示第2位→显示第…

Android Studio jetpack compose折叠日历日期选择器【折叠日历】

今天写一个日期选择器,大家根据自己需求改代码,记得点赞支持,谢谢~ 这是进入的默认状态 折叠状态选中本周其他日期状态 切换上下周状态 展开日历状态 切换上下月状态 选中状态 代码如下: import android.content.C…

驭码CodeRider 2.0全栈开发实战指南:从零构建现代化电商平台

驭码CodeRider 2.0全栈开发实战指南:从零构建现代化电商平台 一、CodeRider 2.0:重新定义全栈智能开发 1.1 革命性升级亮点 #mermaid-svg-AKjytNB4hD95UZtF {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-AKjyt…

大模型智能体AutoGen面试题及参考答案

目录 AutoGen 的核心是什么? Agent 在 AutoGen 中承担什么角色? AutoGen 是如何定义 AssistantAgent、UserProxyAgent 等代理类型的? 什么是 GroupChat(组对话)模式? AutoGen 的 system message 在框架中扮演什么作用? 如何通过 Agent 实现自然语言处理? AutoGen…

深度学习笔记26-天气预测(Tensorflow)

🍨 本文为🔗365天深度学习训练营中的学习记录博客🍖 原作者:K同学啊 一、前期准备 1.数据导入 import numpy as np import pandas as pd import warnings import seaborn as sns import matplotlib.pyplot as plt warnings.filt…

day54 python对抗生成网络

目录 一、GAN对抗生成网络思想 二、实践过程 1. 数据准备 2. 构建生成器和判别器 3. 训练过程 4. 生成结果与可视化 三、学习总结 一、GAN对抗生成网络思想 GAN的核心思想非常有趣且富有对抗性。它由两部分组成:生成器(Generator)和判…

龙虎榜——20250613

上证指数放量下跌收阴线,个股下跌超4000只,受外围消息影响情绪总体较差。 深证指数放量下跌,收阴线,6月总体外围风险较高,转下跌走势的概率较大,注意风险。 2025年6月13日龙虎榜行业方向分析 1. 石油石化&…

Linux常用命令加强版替代品

Linux常用命令加强版替代品 还在日复一日地使用 ls、grep、cd 这些“上古”命令吗?是时候给你的终端来一次大升级了!本文将为你介绍一系列强大、高效且设计现代的Linux命令行工具,它们将彻底改变你的工作流,让你爱上在终端里操作…

Hadoop 003 — JAVA操作MapReduce入门案例

MapReduce入门案例-分词统计 文章目录 MapReduce入门案例-分词统计1.xml依赖2.编写MapReduce处理逻辑3.上传统计文件到HDFS3.配置MapReduce作业并测试4.执行结果 1.xml依赖 <dependency><groupId>org.apache.hadoop</groupId><artifactId>hadoop-commo…

Python打卡第53天

浙大疏锦行 作业&#xff1a; 对于心脏病数据集&#xff0c;对于病人这个不平衡的样本用GAN来学习并生成病人样本&#xff0c;观察不用GAN和用GAN的F1分数差异。 import pandas as pd import numpy as np import torch import torch.nn as nn import torch.optim as optim from…

力扣-279.完全平方数

题目描述 给你一个整数 n &#xff0c;返回 和为 n 的完全平方数的最少数量 。 完全平方数 是一个整数&#xff0c;其值等于另一个整数的平方&#xff1b;换句话说&#xff0c;其值等于一个整数自乘的积。例如&#xff0c;1、4、9 和 16 都是完全平方数&#xff0c;而 3 和 1…

前端构建工具Webapck、Vite——>前沿字节开源Rspack详解——2023D2大会

Rspack 以下是针对主流构建工具&#xff08;Webpack、Vite、Rollup、esbuild&#xff09;的核心不足分析&#xff0c;以及 Rspack 如何基于这些痛点进行针对性改进 的深度解析&#xff1a; 一、主流构建工具的不足 1. Webpack&#xff1a;性能与生态的失衡 核心问题 冷启动慢…

输入法,开头输入这U I V 三个字母会不显示 任何中文

1. 汉语拼音规则的限制 汉语拼音中不存在以“V”“U”“I”为声母的情况&#xff1a; 汉语拼音的声母是辅音&#xff0c;而“V”“U”“I”在汉语拼音中都是元音&#xff08;或韵母的一部分&#xff09;。汉语拼音的声母系统中没有“V”“U”“I”作为声母的音节。例如&#xf…

Linux文件权限详解:从入门到精通

前言 权限是什么&#xff1f; 本质&#xff1a;无非就是能做和不能做什么。 为什么要有权限呢&#xff1f; 目的&#xff1a;为了控制用户行为&#xff0c;防止发生错误。 1.权限的理解 在学习下面知识之前要先知道的一点是&#xff1a;linux下一切皆文件&#xff0c;对li…

在多云环境透析连接ngx_stream_proxy_protocol_vendor_module

1、模块定位与价值 多云接入&#xff1a;在同一 Nginx 实例前端接入来自多云平台的私有链路时&#xff0c;能区分 AWS、GCP、Azure 特有的连接 ID。安全审计&#xff1a;自动记录云平台侧的 Endpoint/VPC ID&#xff0c;有助于联调和安全事件追踪。路由分流&#xff1a;基于不…

力扣:基本计算器

基本计算器: 224. 基本计算器 - 力扣&#xff08;LeetCode&#xff09; 本体思路为&#xff0c;将中缀表达式转为后缀表达式&#xff0c;通过后缀表达式进行运算。 中缀表达式: 我们日常生活中熟知的表达式如12-30 就是一个中缀表达式。 后缀表达式: 150. 逆波兰表达式求值 - …

《AI日报 · 0613|ChatGPT支持导出、Manus免费开放、GCP全球宕机》

AI 资讯 1️⃣ OpenAI ChatGPT Canvas新增多格式导出功能 OpenAI终于为ChatGPT Canvas推出了用户期待已久的导出功能。现在,用户可以将创作内容导出为多种格式:文档类支持PDF、docx和markdown格式,代码文件则可直接保存为对应扩展名的源文件(如.py、.js、.sql等)。这一功…

C++中的零拷贝技术

一、C中零拷贝技术的核心概念 零拷贝&#xff08;Zero-copy&#xff09;是一种重要的优化技术&#xff0c;旨在减少数据在内存中的不必要复制&#xff0c;从而提高程序性能、降低内存使用并减少CPU消耗。在C中&#xff0c;零拷贝技术通过多种方式实现&#xff0c;包括引用语义…

RT_Thread内核源码分析(五)——内存管理@小堆内存管理算法

目录 1、内存堆控制 1.1 内存堆控制器 1.2 内存块节点 1.3 内存堆管理 2、内存堆初始化 2.1 初始化接口 2.2 初始化示例 2.3 源码分析 3、内存堆操作 3.1 内存块申请 3.1.1 相关接口 3.1.2 原理分析 3.1.3 示例分析 3.1.4 代码分析 3.2 内存块伸缩 3.2.1 相关…

MyBatis-Plus 混合使用 XML 和注解

mybatisplus代码生成器&#xff1a; 版本匹配是个比较麻烦的问题&#xff0c;这是我的配置&#xff1a; <dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-boot-starter</artifactId><version>3.5.2</version>…