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的流程,将会被阻塞接管。

每天积累一点点

文章代码

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

相关推荐

UDS 诊断 - CommunicationControl(通信控制)(0x28)服务 诊断

字节参数名称字节值#1CommunicationControl(通信控制)请求SID0x28#2#3communicationType(通信类型)#4nodeIdentificationNumer(节点标识符)(高位字节)controlType 为 0x04 或 0x05 时,使用该参数#5nodeIdentificationNumer(节点标识符)(低位字节)controlType 为 0x04 或 0x05 时,使用该参数。

耳朵东金子千的博客 1821

(转)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

【Telephony】APN配置及加载流程&项目实践(AOSP)

学习APN关键是学会明白apns-conf.xml文件各个参数的意思,然后熟悉TelephonyProvider是如何读取文件创建数据库的,譬如数据库表的约束、文件的读写顺序等,还有读取后的ApnSetting的二次定制以及显示(例如屏蔽xcap等)。虽然一般在项目中APN的需求很简单,就是按照客户提供的APN参数添加上配置,或者在显示屏蔽一些参数,但是项目维护的过程中APN的问题也是比较多的。

weixin_44513784的博客 5513

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

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

a6265655的专栏 333

拆解汽车T-Box:从4G模块到MCU,手把手教你理解车联网的‘黑匣子’

本文深入拆解汽车T-Box,揭示其作为车联网‘黑匣子’的核心架构与工作原理。从4G模块、MCU主控到外设接口,详细解析各组件功能及协同机制,并通过实战案例展示故障诊断与性能优化技巧,帮助读者全面理解T-Box在智能汽车中的关键作用。

weixin_30955617的博客 497

Ansys Circuit新手必看:IBIS模型导入时,Pin Import和Buffer Import到底怎么选?

本文详细解析了Ansys Circuit中IBIS模型导入时Pin Import与Buffer Import的选择策略,帮助SI/PI工程师根据仿真需求做出正确决策。通过对比两种方式的特性差异和应用场景,提供实用的选择指南和性能优化建议,提升信号完整性仿真效率。

weixin_26846533的博客 261

Sychronized底层原理及特性详解

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

a15119273009的博客 1418

Vue3 - 实现用户上传本地视频文件到服务器+上传进度条+暂停续传+中断取消上传等,视频上传后可在线播放预览已上传的视频,视频文件上传视频回显示例代码(支持Element、Antd等upload组件

实现视频上传和视频预览,vue3实现视频上传功能,上传视频回显,视频流上传播放功能,vue与axios上传视频并显示上传进度,upload组件实现视频文件上传视频回显,upload组件实现视频上传及进度条展示,视频上传及实时进度条显示,视频上传过程中如何中断上传,VUE3+element 上传视频,大文件分片上传+断点续传,axios大文件切片上传,Vue3上传视频后如何播放预览,上传本地视频到后端服务器及在线预览播放视频+移除视频+进度条上传,将已选视频上传服务端并可预览视频,详细实现选择视频并上传到服务

🏆 前端领域优质创作者 3115

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

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

蓝叶菱的专栏 4412

华为OD机试双机位A卷 - 小明减肥 (C++ & Python & JAVA & JS & GO)

华为OD机试2025双机位A卷 小明减肥,提供Java、C++、Python、Go、JavaScript源码实现,并提供完整思路讲解

qq_45776114的博客 709

软考备考:《系统规划与管理师教程》(第2版)第2章——数字ZG与数智化发展

🔴数字化转型的定义:数字化转型是建立在数字化转换、数字化升级基础上,进一步触及组织核心业务,以新建一种业务模式为目标的高层次转型。

✨ 欢迎来到【Seal ^_^ 的优快云博客】!✨ 960

Synchronzied 的深入理解 (上)

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

CandyCCCation的博客 637

大学计算 - NumPy库的矩阵运算 实训答案

本文介绍了三个NumPy矩阵操作的实验练习。第一关练习ndarray类型操作,包括矩阵构造、元素平方和计算、矩阵乘积运算和逆矩阵行列式计算;第二关针对matrix类型,涉及绝对值最大值获取、特定矩阵乘积运算和转置矩阵特征值计算;第三关实现Moore-Penrose伪逆矩阵计算,并与NumPy内置pinv函数结果比较误差。实验涵盖了NumPy核心矩阵操作,展示了从基础运算到高级矩阵分解的应用,帮助掌握科学计算中的矩阵处理技巧。

2403_89314916的博客 310

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

synchronzied底层原理,优化

cloud_fly的博客 1299

怀旧版电脑GBC模拟器

再现原版GBC掌机的界面,电脑运行连游戏界面都一样大小,很怀旧,很真实,(英文版)强烈推荐,很小的文件,很难才找到的。

LinePhone3.2.7(android端最新版本) sip客户端

SIP客户端For android:LinePhone3.2.7(最新版本),官网地址:http://www.linphone.org/

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

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

SmartCodeTech的博客 718

Outlook Express 6 (OE6) for Win7/Win8/Win10

英文版 Outlook Express6.0, 兼容Win7/Win10/WinXP,解压就可用。

彻底理解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

RU DOS版、EFI版32bit/64bit BIOS常用工具

DOS版、EFI版的RU工具,可用来查看IO、MEM、PCI、SMBIOS等资源。亲测在EFI SHELL下可用。

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、付费专栏及课程。

余额充值