Synchronzied 的深入理解(下)

Screenshot_2026-11-03-16-23-09-34.jpg 立即下载
Sychronized深入分析

话接上文,我们需要想要知道为什么所有的对象都可以成为sychronized的锁,然后有一张描述对象在内存中的布局。

在这里插入图片描述

首先对于Java而言,我们创建实例的方法可以通过new一个对象,而对于JVM而言,这句话会被解释成,创建一个instanceOopDesc对象,虚拟机使用OOP-KLASS模型来描述Java对象实例。

  • OOP(Ordinary Object Point)指的是普通对象指针
  • Klass 用来描述对象实例的具体类型。

而虚拟机如果描述普通对象会使用instanceOopDesc而数组类型则使用arrayOopDesc来描述,同时他们都继承自OopDesc,由于底层使用c语言编写,这边就不再展开。接下来就是重点戏,我们将目光锁定在OopDesc中,这里面有两个重要的成员,分别是_mark和_metadata

  • _mark 表示对象标记,是markOop类型,也就是真正用来记录锁标记的Mark Word部分。
  • _metadata 表示类元信息,存储一些对象指向他的类元数据( Klass)。

MarkWord在Mark word中定义了可以存储锁状态的常量,结合之前的话题来将,我们每一个使用new关键字创建出来的,并且由于虚拟机解释成instanceOopDesc对象,都会含有这样一个mark word头,接下来的一系列操作都与这个东西有关。虽然,mark word在32位操作系统和64操作系统表现分别为占32bit和64bit,但是总体的布局是没什么区别的。当锁的状态发生变化的时候,相应markdown中的锁标志位也会发生变化。下面以32bit的markdown作为例子。

在这里插入图片描述

现在,来稍微回顾一下,我们现在正在探究sychronized关键字底层到底是如何加锁的,我们从JVM的层面了解到sychronized关键字的上锁对象可以是任何的对象,然后我们接着明白,在JVM中,new出来的对象将会被解释成 instanceOopDesc或者arrayOopDesc,由于这两者都会继承自oopDesc,所以更多的信息也许可以从父类看起,在父类中,我们终于找到了和锁有关系的标记->_mark,也是Mark Word。

markDown中存在四种锁状态

  • 无锁
    • 暂未有线程获得锁
  • 偏向锁
    • 已经有一个线程进入了同步块时候,锁标志位将会被设置为01,并记录当前已经持有偏向锁线程的Id。
  • 轻量级锁
    • 当已有线程获得偏向锁后,仍有线程尝试获得锁的时候,锁会升级为轻量级锁
  • 重量级锁
    • 当因为锁等待的线程过多,轻量级锁将会膨胀为重量级锁,造成阻塞。

而修改这些锁状态的对象,被称为monitor(监视器对象),他是一个同步对象,任何的Java对象天生就会携带这样一个monitor,所以,多线程去访问被sychronized修饰的方法,或者代码块时,本质上是去争夺对象监视器,然后去修改此对象的锁标记,来达到锁的升级,和独占。

Sychronized 锁升级

上面我简单的解释了一下四种锁的状态,为什么要提出这四种锁的状态呢,明明锁只要一种就可以了不是吗,让其他未获得锁的线程等待不就可以了吗?

早期的Sychronized确实是只有重量级锁,可是后来设计者发现,大部分的线程在获得锁后,会在极短的时间内释放掉,也就是变为无锁状态,明明只要在门口稍微等待一下,我就可以进去了,你却还要阻塞我,这不是有点不太友好呢?

所以,为了优化Sychronized锁的性能,设计者提出了其它的两种锁,偏向锁和轻量级锁。

我们举一个例子。

偏向锁的场景是:线程A进入了同步块,他将会把自己的线程Id挂到偏向锁上,并且设置锁标记为01,表示现在已经有人了。

轻量级锁的场景是:同时来了线程A和线程B,线程A优先于线程B获得了偏向锁进入了同步块,线程B由于没有获得锁,锁将会被升级成轻量级锁。

重量锁的场景是:偏向锁和轻量锁的场景都算不上真正意义上的上锁,而只是修改了锁的标志位,并没有真正的阻塞,当有更多的线程在等待获得锁,并且超过了自旋时间的话,轻量锁将会膨胀为重量级锁,并将后续的线程阻塞。

在这里插入图片描述

偏向锁的获得

我们可以作锁对象(monitor)中的锁一开始应该是无锁状态,因为这个时候,还没有任何线程去获得这个对象监视器。

  • 线程1去判断对象中的Markword是否处于可偏向状态。
    • 如果是可偏向状态,则通过cas操作,将当前线程ID写到Mark Word中去。
      • 如果cas成功,那么markword会带有当前线程的ID并执行后续的同步块
      • 如果cas失败,说明这个时候已经有其他线程获得了偏向锁,这种情况下当前锁就存在竞争,以线程2的视角就是线程1获得锁,而导致线程2无法获得偏向锁。这种情况下,线程2会去尝试撤销偏向锁,并且将它持有的锁升级为轻量级锁,不过这个升级操作需要等待没有线程在执行字节码的时候,才能去做。
    • 如果是已偏向状态,则需要检查markword中存储的线程ID是否等于当前线程的ThreadID
      • 如果相等,不需要获得锁,可在此执行同步代码块。
      • 如果不相等,说明锁偏向于其它线程,需要撤销偏向锁升级到轻量级锁。

偏向锁的撤销

偏向锁的撤销并不是意味着对象恢复成无锁可偏向状态,也就是对于偏向锁的撤销这一概念而言,所谓的撤销,也就意味着下一步可能会被升级为轻量级锁状态。

上图,还是用线程1和线程2来举例,线程2发现获取偏向锁中,cas失败时,会直接将被偏向的锁对象升级到被加了轻量级锁状态。

而对于线程1这个已经持有了偏向锁的线程而言,他要进行偏向锁的撤销操作时,会有两种情况。

  • 线程1已经将同步代码块执行完毕,线程1将会把锁对象的头设置为无锁并且线程2可以cas操作将已经设置成无锁的锁对象偏向自己。
  • 线程1的同步代码块还未执行完毕,这个时候,线程1将会把偏向锁审计为轻量级锁后,再执行后面的同步代码块。

对于偏向锁的总结

偏向锁算是一种乐观锁的实现,他的升级操作只能由当前已经获得了锁的线程来执行。

那么在实际开发中,一般都会存在2个以上线程进行竞争,所以开启偏向锁的意义不大,反而会影响性能,所以我们可以通过JVM参数去关闭他,UseBiasedLocking

轻量级锁

由于种种原因,我们的偏向锁被升级为轻量级锁,那么我们选取一个比较容易理解的场景,在偏向锁升级为轻量级的场景下,是不是意味着至少有两个线程存在锁的竞争,这里的两个线程表示一个线程获得了锁另一个线程没有获得锁情况。

很常见的就是线程1获得偏向锁后,还未执行完同步块,这个时候线程2来了,希望获得锁,但是这个时候cas操作失败,导致线程1将偏向锁升级为了轻量级锁。

可是这个时候,线程2并没有放弃,他还是希望获得锁,即便这个锁已经变成了轻量级锁,如果第一次尝试获得轻量级锁失败(其实这一操作也就是用cas再次尝试替换markword的值),jvm会给他另一个建议,那就是,要不你再试试?的建议,所以线程2将会用自旋锁的方式,多次去尝试去获得锁。

这里的自旋不过是一段无意义的死循环,目的是不希望线程2因为第一次尝试没有获得锁而被挂起,他可能只需要多尝试几次就可以再次获得锁了。

所以,自旋锁的使用场景在于同步块可以执行的非常快,以至于我只要自旋几次就能够撑到获得锁的线程1,离开同步代码块,并且线程2通过cas操作成功将轻量级锁获得。

当然上面是比较理想的情况,如果这个时候,连自旋都无法让线程2获得锁的话,那么轻量级锁将会膨胀为重量级锁并阻塞线程2,毕竟自旋也是比较耗费cpu性能,我不能让你一直尝试这种操作,所以无可奈何,只能将你阻塞了。

在这里插入图片描述

重量级锁

这个时候你可能会有疑问,为什么这里修改锁标志的会是未获得锁的线程,不是说只有获得锁的线程才有资格去修改锁的标志位了,来控制他的升级。

这里就不得不拿出,我们在文章开头谈及到的对象监视器(Monitor)了,对象监视器就是为了重量级锁而存在的,对象监视器也可以叫做同步对象,同时所有的对象天生就携带了monitor,那么就意味着,如果事态已经严重到需要重量级锁出面的地步的话,原有的只是修改markword已经无法满足需求了,所以monitor将会出面维护后续阻塞的线程。

public class SychronziedDemo {

    public static void main(String[] args) {

        synchronized (SychronziedDemo.class){

        }

    }

}

我们可以写一个这样简单的代码块,然后查看.class到底生成了怎样的字节码指令来表示这个代码块。

在这里插入图片描述

加了同步代码块后,我们在字节码中发现了一个monitorenter和monitorexit

从这里开始,我们的sychronized会真正意义上的去阻塞线程了,而monitor将会去维护这些被阻塞的线程。
在这里插入图片描述

到此,sychronized所有锁分析完毕。

回顾

我们来做一个简单的总结

 synchronized (lock){
	// todo
}

其实上述说了那么多,也就是对这一句话的解释,因为这句话很简单,所以底层为我们做了非常多的操作。

倘若现在存在若干个线程去访问这个同步代码块,线程A,线程B,线程C…

  1. 只有线程A会进入临界区(快要进入同步块的地段)
  2. 线程A和线程B交替进入临界区,竞争还行,算不上激烈。
  3. 线程A,线程B,线程C…非常多的线程同时进入临界区,竞争激烈。

偏向锁

线程A进入临界区,将锁对象(lockObject)的对象头 Mark Word的锁标志位设置为01,同时cas将自己的线程ID记录到Mark Word中,进入偏向模式。这之后若线程A再次进入同步块的时候,无需同步操作,就可以进入同步块。

轻量级锁

很显然,偏向锁太过理想化,如果只有一个线程,我们其实不加锁效率会更好。

当线程A和线程B交替进入临界区的时候,因为线程A优先于线程B获得了偏向锁,而线程B再次尝试获得锁的时候,由于线程A还未执行玩同步块,所以他会暂停自己并且升级偏向锁到轻量级锁。同时线程B将会通过自旋转获得轻量锁。

重量级锁

两个线程可能轻量级锁可以解决,但是更多的线程显然就不能满足锁的需求了。

当更多的线程同时进入临界区的时候,轻量锁将会膨胀为重量级锁,因为这个时候,其它的线程会在被monitor维护的同步队列中阻塞,所以,原轻量级锁获得者,也就是现在重量级锁持有者将会把markword的所标记正式更新为10。

在这里插入图片描述

而接下来,将会是monitorexit和monitorenter的流程,将会被阻塞接管。

每天积累一点点

文章代码

研发流程后端开发工具,提示功能开发效率 后端开发工具 立即下载

相关推荐

深入解析“PROJ: proj_create_from_database: Cannot find proj.db”错误:从环境配置到打包实战

本文深入解析了“PROJ: proj_create_from_database: Cannot find proj.db”错误的根源与解决方案,涵盖环境配置、PyInstaller打包及跨平台兼容性处理。通过详细分析PROJ库路径问题及GDAL依赖关系,提供动态路径设置、资源文件打包等实战技巧,帮助开发者高效解决地理数据处理中的常见错误。

weixin_29074409的博客 445

(转)C# Hashtable Synchronized vs SyncRoot

摘自:http://www.afugame.com/club/dispbbs.asp?boardid=13&id=5236& page=1&move=nextSynchronized vs SyncRoot 我们知道,在.net的一些集合类型中,譬如Hashtable和ArrayList,都有Synchronized静态方法和SyncRoot实例方法,他们之间有联系吗?我怎...

weixin_30583563的博客 374

从DIY到实战:手把手教你搭建一个媲美吉时利2450的nA级电流源表(附原理图与避坑指南)

本文详细解析如何从零打造nA级精密电流源,涵盖核心设计原理、PCB布局技巧、热电偶效应应对及校准验证体系。通过低成本替代方案和优化设计,实现媲美商用源表的性能,特别适合硬件爱好者和独立开发者。文章还提供原理图与避坑指南,助你轻松搭建高精度电流源系统。

weixin_42656416的博客 293

归纳一下:C#线程同步的几种方法

我们在编程的时候,有时会使用多线程来解决问题,比如你的程序需要在 后台处理一大堆数据,但还要使用户界面处于可操作状态;或者你的程序需要访问一些外部资源如数据库或网络文件等。这些情况你都可以创建一个子线程去处理, 然而,多线程不可避免地会带来一个问题,就是线程同步的问题。如果这个问题处理不好,我们就会得到一些非预期的结果。   在网上也看过一些关于线程同步的文章,其实线程同步有好...

a6265655的专栏 333

Esp32蓝牙Hid调用方案 http执行设备、鼠标、键盘相关操作

本文主要介绍 Esp32 蓝牙 Hid 相关的多种操作方式,详细的设备、鼠标、键盘相关操作的 http 调用方法。下面是整理出的所有 http 调用方法和 rhino 引擎方式。本机调用的话直接使用http://localhost:8080 如果是局域网调用则使用软件里的地址 比如我的是http://192.168.1.5:8080。文件地址: https://www.123865.com/s/Qtm4jv-xyZbd?使用前需要一块烧录好的ESP32板子并在手机安装好embedded软件用于蓝牙通信。

代码简单说 Vue、JAVA、PHP、Node.js 熟练运用,接口、架构、性能全搞定。 569

109. SAP ABAP 应用程序中的锁机制和工作原理介绍

SAP ABAP 系统中的锁概念,是 SAP ABAP 为了保证`数据一致性`而引入的一种保护机制。 像 SAP 这种复杂的企业级应用,系统中同一时间段的同一份数据,可能会有多个 Transaction(终端用户或者 API )同时进行读写操作。为了防止出现数据不一致,SAP ABAP 引入了锁机制。 这种锁机制并非数据库服务器层面的锁,而是在 SAP ABAP 应用服务器层面上工作,加锁和解锁都由专门的 ABAP Function Module 来完成。

2007 年 ~ 2025 年,深耕 SAP 技术 18 年 1468

Sychronized底层原理及特性详解

特性 原子性 ​ 原子性就是一个操作或者多个操作要么一起执行,要么不执行。比如i++就是不具备原子性,它是先取出i的数据再进行加1,那这个过程在多线程中可能会出现脏读的情况。(Volatile与Sychronized的最大区别就是不具备原子性) 可见性 ​ Sychronzied被加锁对象的锁对于每个线程都是可见的,都能获取到锁的状态,在释放锁之前,会把最新的值更新到主存,保证值都是最新状态。 ​ Volatile修饰的变量,每当值要改变时,都会立即更新到主存中,主存的内容对于其他线程都是可见的,就能保证值

a15119273009的博客 1418

YOLO26优化:注意力魔改 | SKAttention注意力,自适应地调整其感受野大小,效果优于SENet

SKAttention输入自适应地调整其感受野大小的能力

①答疑群聊服务;②YOLO大模型知识问答系统;③计算机视觉论文生成智能体; 250

同步锁定Java synchronized详解(C#翻译)

java synchronized的c#实现 ? lock (new { }.GetType()){} synchronized有两种用法,一种是在方法定义时使用,多线程状态下,这个方法只能同时被同一个线程执行;另一种就是你问到的这种情况,用于锁定代码段,也就是说,{ }括号中的代码是

蓝叶菱的专栏 4412

Marvell 88Q6113 如何制作一个完整的量产固件88Q6113_flash.bin并进行烧录

本章节介绍如何基于Marvell工具链,制作一个完整的量产固件88Q6113_flash.bin,集成用户自定义的配置文件 config.bin,用于88Q6113的Flash烧录。注:Marvell 88Q5072 与 Marvell 88Q6113 的配置流程是一样的。

老板来份猪脚拼叉烧的博客 2011

神经形态芯片:IBM TrueNorth技术详解以及应用案例

2018年的一项演示中,机器人使用TrueNorth处理了复杂的路径规划,功耗仅为传统系统的1/10。它通过模仿人脑的结构,解决了传统芯片在功耗和并行性上的痛点,并在图像识别、物联网和机器人等领域展现了强大潜力。:不同于传统芯片的时钟驱动,TrueNorth是事件驱动的。例如,工厂中的振动传感器使用TrueNorth检测设备故障的早期迹象,实现了更高效的维护策略,避免了停机时间。:在医疗领域,TrueNorth可以用于可穿戴设备,比如一个智能助听器,它能过滤噪音并增强特定声音,功耗低到可以用纽扣电池供电。

远程部署调试 运行安装 项目调试 二次开发 项目技术新 持续迭代 部分源码免费分享 2477

Synchronzied 的深入理解 (上)

为什么要使用多线程 在硬件条件不断进步的今天,我们现在常用的电脑已经不是简单的单核CPU,而是4核、8核、甚至更多。 而如果不使用多线程技术的话,一般我们就只用一个CPU来处理程序上的计算问题,复杂且庞大的计算量全部压在一个CPU上,其它CPU只负责划水,那么这无法物尽其用。 当然我们也不是说可怜这一个CPU,而是真实的环境下,使用多核CPU以及超线程技术可以实现并行,这意味着我们可以在单位时间内...

CandyCCCation的博客 637

Autosar诊断实战系列05-Dcm之0x27加解密服务配置开发详解

UDS 0x27服务实战开发介绍,对解锁尝试次数及delay时间进一步解释。

initiallizer的博客 1993

深入理解synchronzied,底层源码剖析

synchronzied底层原理,优化

cloud_fly的博客 1299

win10 跳过U8环境检测的IE WebControls.rar

win10 安装u8的时候 提示IE WebControls 不通过,这个工具可以欺骗u8的检查。 跳过U8环境检测的IE WebControls。提示导入失败后,重新检测通过。

ssm框架搭建

这个是一个在idea上成功跑起来的ssm框架项目,保存在这里方便以后使用直接下载

【得物技术】深入理解synchronzied底层原理

一、synchronized 简介 synchronized 是 Java 中的关键字,是一种同步锁。在多线程编程中,有可能会出现多个线程同时争抢同一个共享资源的情况,这个资源一般被称为临界资源。这种共享资源可以被多个线程同时访问,且又可以同时被多个线程修改,然而线程的执行是需要 CPU 的资源调度,其过程是不可控的,所以需要采用一种同步机制来控制对共享资源的访问,于是线程同步锁——synchronized 就应运而生了。 二、如何解决线程并发安全问题 多线程并发读写访问临界资源的情况下,是会存在线程

SmartCodeTech的博客 718

文档:亲测可用解决vnc灰屏,黑屏,鼠标大黑叉情况

主要解决vnc-viewer灰屏,黑屏,鼠标大黑叉等现象,详细见文档。

彻底理解synchronized

1. synchronized简介 在学习知识前,我们先来看一个现象: public class SynchronizedDemo implements Runnable { private static int count = 0; public static void main(String[] args) { for (int i = 0; i < ...

weixin_34411563的博客 469

visio安装包

Microsoft Office Visio 是一款专业的制图软件。利用经过更新的形状、协作工具和数据链接图表,创建专业级图表以简化复杂的信息。

C#同步调用sync与异步调用Async

同步调用委托的Invoke方法用来进行同步调用。同步调用也可以叫阻塞调用,它将阻塞当前线程,然后执行调用,调用完毕后再继续向下进行。同步调用的例子:} } 运行结果: * * * * * * * * * * SyncInvokeTest * * * * * * * * * * * * * * Computing 1 + 2 .. . Computing Complete . Do other work .. . .. . .. .异步调用。......

杰西笔记 2543

android synchronized原理分析(二) Monitor

public class SyncLock { public void testBolokCode(){ Thread thread1 = new Thread(new Runnable() { @Override public void run() { synchronized (SyncLock.this){ try{

zhzhangnews的博客 1807

C#多线程 同步方法synchronized

http://www.what21.com/sys/view/csharp-net_11_1473746333228.html

dhsajkd的专栏 2253

synchronized锁升级过程 悲观锁 乐观锁 偏向锁 MarkWord锁标识位

synchronized基础概念 要完成原子性操作,只能靠锁来实现 上锁的本质就是把并发操作转变为序列化操作 修饰方法锁的是对象,修饰静态方法锁的是类的class,class只有一个 抛出异常,锁会被释放 synchronized锁对象时不要用Integer、String、Long类型 Integer和Long会有自动拆箱和装箱的过程,改变值后,可能不会是原来的对象,同步块就会失效 String因为有常量池的存在,值一致时,指向的地址会一致,会出现两个线程要锁的是同一个对象 JDK早期,synchroni

WindNolose的博客 782

volatile关键字的作用(zz)

恐怕比较一下volatile和synchronized的不同是最容易解释清楚的。volatile是变 量修饰符,而synchronized则作用于一段代码或方法;看如下三句get代码: int i1;              int geti1() {return i1;} volatile int i2;  int geti2() {return i2;} int i3;    ...

weixin_33949359的博客 226

C#中volatile的用法

恐怕比较一下volatile和synchronized的不同是最容易解释清楚的。volatile是变量修饰符,而synchronized则作用于一段代码或方法;看如下三句get代码: inti1;intgeti1(){returni1;} volatileinti2;intgeti2(){returni2;} inti3;...

weixin_30296405的博客 397

深入JVM锁机制1-synchronized

目前在Java中存在两种锁机制:synchronized和Lock,Lock接口及其实现类是JDK5增加的内容,其作者是大名鼎鼎的并发专家Doug Lea。本文并不比较synchronized与Lock孰优孰劣,只是介绍二者的实现原理。    数据同步需要依赖锁,那锁的同步又依赖谁?synchronized给出的答案是在软件层面依赖JVM,而Lock给出的方案是在硬件层面依赖特殊的CPU指令...

SAN_YUN的专栏 1896

synchronsized修饰方法的使用

线程同步是指对共享资源,若不是共享资源则不涉及线程同步问题。synchronized方法可以锁定对象,也可以锁定代码块。当锁定对象时,修饰在类的方法前面。记住:若两个线程同时访问一个对象的不同方法,且对象有一个synchronized方法和一个非synchronized方法,则是两个线程并不涉及同步问题。只有当访问同一个对象的synchronsized...

weixin_34007291的博客 171
上一篇: Synchronzied 的深入理解 (上)
下一篇: Volatile 详解
PopCandier
博客等级 码龄9年 19粉丝 17原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值