Java并发——Synchronized及其实现原理

简介: Synchronized是Java中实现进程同步最简单的一种方式,`Synchronized`主要有以下三个作用:保证互斥性、保证可见性、保证顺序性 接下来我们就来看下Synchronized的实现原理,看看Synchronized是如何实现上述三个作用的。

Synchronized是Java中实现进程同步最简单的一种方式,Synchronized主要有以下三个作用:

  • 保证互斥性
  • 保证可见性
  • 保证顺序性

接下来我们就来看下Synchronized的实现原理,看看Synchronized是如何实现上述三个作用的。

Synchronized有三种常用方法:

  • 修饰普通方法
  • 修饰静态方法
  • 修饰代码块

具体的用法这里就不展开了,不熟悉的朋友建议先试用一下。

1 Synchronized实现原理

先来看下下面这段代码:

public class SynchronizedDemo {
     public void method() {
         synchronized (this) {
             System.out.println("Method 1 start");
         }
     }
 }

这是一个典型的Synchronized修饰代码块的例子,Synchronized的对象是实例对象。意味着对一个SynchronizedDemo对象,同一时间内只能有一个线程可以执行method方法。

接下来我们来看看上述代码对应的字节码:

这里写图片描述

可以看到synchronized所覆盖的代码块的首尾部分分别加上了monitorentermonitorexit。JVM中对于上述两条指令的解释如下:

  • monitorenter:

Each object is associated with a monitor. A monitor is locked if and only if it has an owner. The thread that executes monitorenter attempts to gain ownership of the monitor associated with objectref, as follows:
• If the entry count of the monitor associated with objectref is zero, the thread enters the monitor and sets its entry count to one. The thread is then the owner of the monitor.
• If the thread already owns the monitor associated with objectref, it reenters the monitor, incrementing its entry count.
• If another thread already owns the monitor associated with objectref, the thread blocks until the monitor's entry count is zero, then tries again to gain ownership.

大意就是:

每一个对象有一个相关联的监视器(monitor),当monitor被占用时便被锁定住。当执行monitorenter执行时,当前线程尝试获取monitor

  • 如果当前monitor的进入数为0,则说明monitor未被占用,则当前线程占有该monitor,同时monitor的进入数变为1
  • 如果当前monitor进入数不为0,且monitor的占用者是当前线程,则可以重复占用该monitor,同时进入数加1
  • 如果其他线程占有了该monitor,则线程进入阻塞状态,直到monitor的进入数重新变为0,再重新尝试获取所有权

  • monitorexit:

The thread that executes monitorexit must be the owner of the monitor associated with the instance referenced by objectref.
The thread decrements the entry count of the monitor associated with objectref. If as a result the value of the entry count is zero, the thread exits the monitor and is no longer its owner. Other threads that are blocking to enter the monitor are allowed to attempt to do so.

执行monitorexit的线程必须是objectref所对应的monitor的所有者。

指令执行时,monitor的进入数减1,如果减1后进入数为0,那线程退出monitor,不再是这个monitor的所有者。其他被这个monitor阻塞的线程可以尝试去获取这个 monitor 的所有权

通过上述两段描述便不难理解Synchronized的实现原理了。Synchronized实际是通过关联一个对象的monitor来实现的。在前文的代码demo中,关联的对象实际就是SynchonizedDemo实例对象的monitor。其实wait/notify等方法也依赖于monitor对象,这就是为什么只有在同步的块或者方法中才能调用wait/notify等方法,否则会抛出java.lang.IllegalMonitorStateException的异常的原因。

接下来再来看看Synchronized修饰方法的场景:

public class SynchronizedMethod {
     public synchronized void method() {
         System.out.println("Hello World!");
     }
 }

对应的字节码为:

这里写图片描述

从反编译的结果来看,方法的同步并没有通过指令monitorenter和monitorexit来完成(理论上其实也可以通过这两条指令来实现),不过相对于普通方法,其常量池中多了ACC_SYNCHRONIZED标示符。JVM就是根据该标示符来实现方法的同步的:当方法调用时,调用指令将会检查方法的 ACC_SYNCHRONIZED 访问标志是否被设置,如果设置了,执行线程将先获取monitor,获取成功之后才能执行方法体,方法执行完后再释放monitor。在方法执行期间,其他任何线程都无法再获得同一个monitor对象。 其实本质上没有区别,只是方法的同步是一种隐式的方式来实现,无需通过字节码来完成。

当Synchronized修饰不同方法时,获取的monitor对象不同:

  • 修饰对象方法:此时获取的monitor为实例对象(即this)的monitor
  • 修饰类方法:此时获取的monitor为类class对象(如Synchronized.class)的monitor

通过上述解释,相信大家已经知道Synchronized是如何保证互斥性的,那么其是怎么实现可见性和顺序性的呢?其实也跟monitor相关:

  • 可见性:执行到monitorenter时,线程会重新从主内存中将数据同步到本地工作内存,从而保证其可以看到其他线程的修改。同时执行monitorexit时,线程也会将本地工作内存的数据同步到主内存中
  • 有序性:monitorenter、monitorexit修饰的代码将禁止进行重排序
相关文章
|
18天前
|
算法 Java 程序员
Java中的Synchronized,你了解多少?
Java中的Synchronized,你了解多少?
|
17天前
|
Java
让星星⭐月亮告诉你,Java synchronized(*.class) synchronized 方法 synchronized(this)分析
本文通过Java代码示例,介绍了`synchronized`关键字在类和实例方法上的使用。总结了三种情况:1) 类级别的锁,多个实例对象在同一时刻只能有一个获取锁;2) 实例方法级别的锁,多个实例对象可以同时执行;3) 同一实例对象的多个线程,同一时刻只能有一个线程执行同步方法。
12 1
|
17天前
|
Java
【编程进阶知识】揭秘Java多线程:并发与顺序编程的奥秘
本文介绍了Java多线程编程的基础,通过对比顺序执行和并发执行的方式,展示了如何使用`run`方法和`start`方法来控制线程的执行模式。文章通过具体示例详细解析了两者的异同及应用场景,帮助读者更好地理解和运用多线程技术。
22 1
|
2月前
|
存储 缓存 Java
java线程内存模型底层实现原理
java线程内存模型底层实现原理
java线程内存模型底层实现原理
|
20天前
|
Java 开发者
在 Java 多线程编程中,Lock 接口正逐渐取代传统的 `synchronized` 关键字,成为高手们的首选
【10月更文挑战第6天】在 Java 多线程编程中,Lock 接口正逐渐取代传统的 `synchronized` 关键字,成为高手们的首选。相比 `synchronized`,Lock 提供了更灵活强大的线程同步机制,包括可中断等待、超时等待、重入锁及读写锁等高级特性,极大提升了多线程应用的性能和可靠性。通过示例对比,可以看出 Lock 接口通过 `lock()` 和 `unlock()` 明确管理锁的获取和释放,避免死锁风险,并支持公平锁选择和条件变量,使其在高并发场景下更具优势。掌握 Lock 接口将助力开发者构建更高效、可靠的多线程应用。
22 2
|
2月前
|
Java API 容器
JAVA并发编程系列(10)Condition条件队列-并发协作者
本文通过一线大厂面试真题,模拟消费者-生产者的场景,通过简洁的代码演示,帮助读者快速理解并复用。文章还详细解释了Condition与Object.wait()、notify()的区别,并探讨了Condition的核心原理及其实现机制。
|
16天前
|
安全 Java 开发者
java的synchronized有几种加锁方式
Java的 `synchronized`通过上述三种加锁方式,为开发者提供了从粗粒度到细粒度的并发控制能力,满足了不同场景下的线程安全需求。合理选择加锁方式对于提升程序的并发性能和正确性至关重要,开发者应根据实际应用场景的特性和性能要求来决定使用哪种加锁策略。
10 0
|
2月前
|
存储 安全 Java
Java并发编程之深入理解Synchronized关键字
在Java的并发编程领域,synchronized关键字扮演着守护者的角色。它确保了多个线程访问共享资源时的同步性和安全性。本文将通过浅显易懂的语言和实例,带你一步步了解synchronized的神秘面纱,从基本使用到底层原理,再到它的优化技巧,让你在编写高效安全的多线程代码时更加得心应手。
|
2月前
|
缓存 Java 编译器
JAVA并发编程synchronized全能王的原理
本文详细介绍了Java并发编程中的三大特性:原子性、可见性和有序性,并探讨了多线程环境下可能出现的安全问题。文章通过示例解释了指令重排、可见性及原子性问题,并介绍了`synchronized`如何全面解决这些问题。最后,通过一个多窗口售票示例展示了`synchronized`的具体应用。
|
3月前
|
传感器 C# 监控
硬件交互新体验:WPF与传感器的完美结合——从初始化串行端口到读取温度数据,一步步教你打造实时监控的智能应用
【8月更文挑战第31天】本文通过详细教程,指导Windows Presentation Foundation (WPF) 开发者如何读取并处理温度传感器数据,增强应用程序的功能性和用户体验。首先,通过`.NET Framework`的`Serial Port`类实现与传感器的串行通信;接着,创建WPF界面显示实时数据;最后,提供示例代码说明如何初始化串行端口及读取数据。无论哪种传感器,只要支持串行通信,均可采用类似方法集成到WPF应用中。适合希望掌握硬件交互技术的WPF开发者参考。
65 0