深入理解synchronized实现原理

简介: 本文深入讲解了Java中`synchronized`关键字的实现原理。`synchronized`确保同一时刻只有一个线程能进入临界区,并保证共享变量的内存可见性。它通过monitor机制实现,作用于方法时使用ACC_SYNCHRONIZED标志,作用于代码块时使用monitorenter和monitorexit指令。每个对象都有一个与之关联的monitor,线程需获取monitor锁才能执行同步代码。Monitor内部包含_EntryList、_Owner、_WaitSet等队列,管理线程的加锁、等待和唤醒过程。

前言

java中的锁大家很快就能想到synchronized和lock,那么synchronized实现原理是怎样呢?本文将深入讲解synchronized实现原理。

简介

synchronized关键字保证方法或者代码块在运行时,同一时刻只有一个方法可以进入临界区,同时它还可以保证共享变量的内存可见性。

实现原理

synchronized 可以作用于方法和代码快,具体如下图:

作用于方法和代码块的实现原理是不同的,代码块采用的是monitorenter、monitorexit指令。而方法采用的是ACC_SYNCHRONIZED指令。

作用于代码块

csharp

代码解读

复制代码

  public synchronized void test1(){
     synchronized (this)
    {
          System.out.println("this is test method");
    }
  }

说明:从编译后的源码可以得知,代码块的同步jvm的实现采用的是关键字monitorenter、monitorexit。每个对象都与一个monitor相关联。当且仅当拥有所有者时(被拥有),monitor才会被锁定。执行到monitorenter指令的线程,会尝试去获得对应的monitor。

加锁的实现

说明:加锁采用是monitorenter指令,每个对象维护着一个记录着被锁次数的计数器, 对象未被锁定时,该计数器为0。线程进入monitor(执行monitorenter指令)时,会把计数器设置为1.当同一个线程再次获得该对象的锁的时候,计数器再次自增.当其他线程想获得该monitor的时候,就会阻塞,直到计数器为0才能成功。

释放锁的实现

说明:释放锁采用的是monitorexi指令,线程执行monitorexit指令,就会让monitor的计数器减一。如果计数器为0,表明该线程不再拥有monitor。其他线程就允许尝试去获得该monitor。

作用于方法

说明:当调用一个设置了ACC_SYNCHRONIZED标志的方法,执行线程需要先获得monitor锁,然后开始执行方法,方法执行之后再释放monitor锁,当方法不管是正常return还是抛出异常都会释放对应的monitor锁。实现的流程图如下:

小结

不管是作用于方法还是同步块,其加锁的原理都是通过monitor来实现的,那么monitor如何实现呢?

Monitor实现原理

关于Java Monitor的工作机理如图所示:

  • 获取monitor的线程,会进入_EntryList队列。
  • 当某个线程获取到对象的monitor后,进入_Owner区域,设置为当前线程,同时计数器_count加1。
  • 如果线程调用了wait()方法,则会进入_WaitSet队列。它会释放monitor锁,即将_owner赋值为null,_count自减1,进入_WaitSet队列阻塞等待。
  • 如果其他线程调用 notify() / notifyAll() ,会唤醒_WaitSet中的某个线程,该线程再次尝试获取monitor锁,成功即进入_Owner区域。
  • 同步方法执行完毕了,线程退出临界区,会将monitor的owner设为null,并释放监视锁。

小结

虽然monitor我们已经知道了相关的实现原理,但是monitor与对象又是如何进行关联呢?

对象与monitor关联

对象与monitor的关联其原理图如下:

对象结构

在虚拟机中,对象在内存中存储的布局可以分为3块区域:对象头、实例数据和对象填充。

  • 实例数据:对象真正存储的有效信息,存放类的属性数据信息,包括父类的属性信息;
  • 对齐填充:由于虚拟机要求 对象起始地址必须是8字节的整数倍。填充数据不是必须存在的,仅仅是为了字节对齐。
  • 对象头:Hotspot虚拟机的对象头主要包括两部分数据:Mark Word(标记字段)、Class Pointer(类型指针)。

对象头

而对象头又分为:Mark Word、类型指针、数组长度。

  • Mark Word:主要用于存储自身运行时数据
  • 类型指针:指向方法区中该 class的对象,JVM 通过此字段来判断当前对象是哪个类的实例
  • 数组长度:当且仅当对象是数组时才会有该字段。

Mark Word

Mark Word 用于存储对象自身的运行时数据,包含哈希码(HashCode)、GC分代年龄、锁状态标志、线程持有的锁、偏向线程 ID、偏向时间戳等。

说明:当锁其膨胀成重量级锁后,其他竞争的线程进来就不会自旋了,而是直接阻塞等待,并且 Mark Word 中的内容会变成一个监视器(monitor)对象,用来统一管理排队的线程。而monitor对象与每个对象都会关联。monitor对象本质上是一个同步机制,保证了同时只有一个线程能够进入临界区,在 HotSpot的虚拟机中,是由C++类ObjectMonitor实现的。

总结

本文对于synchronized的原理进行详细的讲解,关于synchronized锁的升级将在后续的文章进行讲解。如有疑问请随时反馈。


转载来源:https://juejin.cn/post/7139424781585088549

相关文章
|
存储 监控 安全
Synchronized 实现原理
Synchronized 实现原理
199 0
|
11月前
|
存储 缓存 人工智能
【原理】【Java并发】【synchronized】适合中学者体质的synchronized原理
本文深入解析了Java中`synchronized`关键字的底层原理,从代码块与方法修饰的区别到锁升级机制,内容详尽。通过`monitorenter`和`monitorexit`指令,阐述了`synchronized`实现原子性、有序性和可见性的原理。同时,详细分析了锁升级流程:无锁 → 偏向锁 → 轻量级锁 → 重量级锁,结合对象头`MarkWord`的变化,揭示JVM优化锁性能的策略。此外,还探讨了Monitor的内部结构及线程竞争锁的过程,并介绍了锁消除与锁粗化等优化手段。最后,结合实际案例,帮助读者全面理解`synchronized`在并发编程中的作用与细节。
823 8
【原理】【Java并发】【synchronized】适合中学者体质的synchronized原理
|
canal 缓存 NoSQL
Redis缓存与数据库如何保证一致性?同步删除+延时双删+异步监听+多重保障方案
根据对一致性的要求程度,提出多种解决方案:同步删除、同步删除+可靠消息、延时双删、异步监听+可靠消息、多重保障方案
Redis缓存与数据库如何保证一致性?同步删除+延时双删+异步监听+多重保障方案
|
消息中间件 NoSQL 架构师
招行面试:亿级秒杀,超卖问题+少卖问题,如何解决?(图解+秒懂+史上最全)
45岁资深架构师尼恩在读者交流群中分享了如何系统化解决高并发下的库存抢购超卖少买问题,特别是针对一线互联网企业的面试题。文章详细解析了秒杀系统的四个阶段(扣库预扣、库存扣减、支付回调、库存补偿),并通过Redis分布式锁和Java代码示例展示了如何防止超卖。此外,还介绍了使用RocketMQ延迟消息和xxl-job定时任务解决少卖问题的方法。尼恩强调,掌握这些技术不仅能提升面试表现,还能增强实际项目中的高并发处理能力。相关答案已收入《尼恩Java面试宝典PDF》V175版本,供后续参考。
|
消息中间件 NoSQL Kafka
订单超时取消的11种方式(非常详细清楚)
订单超时取消的11种方式(非常详细清楚)
8738 5
订单超时取消的11种方式(非常详细清楚)
|
消息中间件 存储 Java
吃透 RocketMQ 消息中间件,看这篇就够了!
本文详细介绍 RocketMQ 的五大要点、核心特性及应用场景,涵盖高并发业务场景下的消息中间件关键知识点。关注【mikechen的互联网架构】,10年+BAT架构经验倾囊相授。
吃透 RocketMQ 消息中间件,看这篇就够了!
|
开发工具 git
成功解决 IDEA 2020 版本 代码报错不提示的几种方案
这篇文章提供了几种解决IntelliJ IDEA 2020版本中代码报错不提示问题的方案,包括通过修改文件夹权限、暂存本地更改后进行git pull,以及在git pull后应用暂存的更改并提交代码到远程仓库的方法。
(详细图解过程) IDEA在创建类的的时候自动生成作者信息、时间等信息
这篇文章介绍了如何在IntelliJ IDEA中设置文件和代码模板,以便在创建新类时自动生成包含作者信息、日期和时间等信息的文件头。
(详细图解过程) IDEA在创建类的的时候自动生成作者信息、时间等信息
|
缓存 监控 负载均衡
将近2万字的Dubbo原理解析,彻底搞懂dubbo
市面上有很多基于RPC思想实现的框架,比如有Dubbo。今天就从Dubbo的SPI机制、服务注册与发现源码及网络通信过程去深入剖析下Dubbo。
29708 9
|
存储 缓存 算法
图解LinkedHashMap原理
图解LinkedHashMap原理
335 0