深入理解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

相关文章
|
7月前
|
Java
【多线程系列】你先说说synchronized的实现原理
面试官:听说你精通多线程,那我就考考你吧面试官:不用慌尽管说,错了也没关系😊。。。❤️。
【多线程系列】你先说说synchronized的实现原理
|
7月前
|
Java 编译器
synchronized原理
synchronized原理
|
7月前
|
存储 监控 安全
Synchronized 实现原理
Synchronized 实现原理
64 0
|
7月前
|
安全 Java
Java并发编程:Synchronized及其实现原理
Java并发编程:Synchronized及其实现原理
59 4
|
2月前
|
安全 Java
Synchronized是怎么实现的?
Synchronized是怎么实现的?
|
2月前
|
Java 编译器
synchronized 原理分析!
本文从字节码角度剖析`synchronized`关键字的工作原理,介绍其依赖的Java对象监视器锁机制,以及锁的获取、释放过程。文章还详细解释了偏向锁、轻量级锁等优化手段,并通过实例展示了同步方法和代码块的字节码实现。
36 0
|
4月前
|
存储 Java 开发者
synchronized源码分析解读
该文章主要探讨了Java中synchronized关键字的工作原理及其相关的源码分析,概括了synchronized关键字的基本概念、特性和其实现机制。通过源码分析进一步揭示了synchronized背后的运作原理。
|
6月前
|
存储 Java C++
Synchronized底层原理
Synchronized底层原理
|
存储 安全 Java
synchronized 的底层原理
synchronized 的底层是通过 Java 中的监视器锁(monitor)来实现的。每个 Java 对象都有一个与之对应的监视器锁,当一个线程获取了该对象的监视器锁,就可以执行 synchronized 代码块或方法。其他线程只能等待该线程释放锁,才能获取该对象的监视器锁并执行 synchronized 代码块或方法。
108 0
synchronized 的底层原理
|
Java API 调度
synchronized 和 ReentrantLock 的实现原理是什么?它们有什么区别
synchronized 和 ReentrantLock 的实现原理是什么?它们有什么区别
89 0

热门文章

最新文章