从MESA模型到锁升级:synchronized性能逆袭的底层逻辑
管程(Monitor)是一种用于管理共享资源访问的程序结构,能确保同一时刻只有一个线程访问共享资源,解决并发编程中的互斥和同步问题。MESA模型是管程的经典实现,主要由入口等待队列和条件变量等待队列构成。
1)入口等待队列:确保线程互斥,多个线程试图进入管程时,仅一个线程能成功,其余线程在入口等待队列中排队。
2)条件变量等待队列:解决线程同步问题,线程在管程内执行时,若条件不满足需等待其他线程操作结果,则进入相应条件变量的等待队列。
当线程被notify或notifyAll唤醒后,不会立即执行,而是先进入入口等待队列竞争管程的锁。只有竞争到锁后,线程才能继续执行。因此,被唤醒的线程需循环检验条件是否满足,即采用while (条件不满足) { wait(); } 的编程范式,以避免条件不一致问题。

synchronized参考了MESA管程模型,对MESA模型进行了精简。在MESA 模型中,一个管程可以有多个条件变量,而Java中的synchronized机制只对应一个条件变量。
public class Test {
public static void main(String[] args) {
// 同步代码块方式加锁
synchronized (Test.class) {
}
// 同步方法方式加锁
func();
}
public static synchronized void func() {
}
}
先使用javac编译,生成Test.class的文件。使用javap -c命令来查看字节码。
public static void main(java.lang.String[]);
descriptor: ([Ljava/lang/String;)V
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=2, locals=3, args_size=1
0: ldc #2 // class com/tencent/trpcprotocol/dayu/identify/Test
2: dup
4: monitorenter
6: monitorexit
12: monitorexit
15: invokestatic #3 // Method func:()V
18: return
public static synchronized void func();
descriptor: ()V
flags: ACC_PUBLIC, ACC_STATIC, ACC_SYNCHRONIZED
Code:
stack=0, locals=0, args_size=0
0: return
LineNumberTable:
line 25: 0
从字节码可以看出,同步代码块通过monitorenter和monitorexit指令实现锁的获取与释放,而同步方法通过ACC_SYNCHRONIZED标记隐式管理锁。无论采用哪种方式,其本质是对一个对象(Object)的监视器锁(Monitor locking)进行获取,它与synchronized 所在的对象一一对应。
当一个线程进入一个synchronized方法或代码块时,它会尝试获取该对象的监视器锁。如果锁没有被其他线程占用,该线程会获取到锁,并执行临界区的代码。如果锁已经被其他线程占用,该线程会进入阻塞(BLOCKED)状态,并进入同步队列等待锁的释放。
Java 中的 Object 类提供了wait()、notify() 和 notifyAll()方法,这些方法正是基于MESA 模型中的条件变量实现的。当线程执行wait()方法时,会释放锁,并将线程从运行状态转移到等待队列中;当线程被notify或notifyAll 唤醒后,会重新进入同步队列,参与锁的竞争,竞争成功后才能继续执行。

synchronized性能优化
synchronized在早期仅支持重量级锁(Mutex locking),依赖操作系统内核态与用户态的切换,性能较差。JDK 6后引入多级锁优化。
1)偏向锁(Biased Locking):针对同一线程反复获取同一锁的场景,偏向锁会记录首次获取锁的线程ID。后续该线程再次获取锁时,无需同步操作即可直接执行,从而消除不必要的锁竞争开销。
2)轻量级锁(Lightweight Locking):通过CAS操作和锁标记位实现。线程尝试以CAS方式将锁标记为轻量级状态,若成功则直接获取锁;若失败,则通过自旋等待锁释放。此机制在竞争不激烈时避免了内核态切换,显著提升锁操作效率。
3)自旋锁(Spin Locking):当锁获取失败时,线程会在有限次数内循环等待(自旋),而非立即进入阻塞状态。适用于锁持有时间极短的场景,通过减少线程挂起与唤醒开销提升性能。
4)锁消除(Lock Elimination):Java虚拟机的即时编译器在运行时分析代码,若检测到某些锁操作(如对局部对象的加锁)无实际意义,会自动移除这些锁,从而优化程序性能。
synchronized使用注意
1)避免死锁:如果两个或多个线程互相等待对方释放锁,会导致死锁。由于synchronized 不提供超时机制,可以使用 JUC并发包的ReentrantLock 并设置超时时间来避免死锁。
public class DeadlockAvoidance {
private final Object lock1 = new Object();
private final Object lock2 = new Object();
// 线程1
public void method1() {
synchronized (lock1) {
System.out.println(Thread.currentThread().getName() + " locked lock1");
synchronized (lock2) {
System.out.println(Thread.currentThread().getName() + " locked lock2");
}
}
}
// 线程2
public void method2() {
synchronized (lock1) {
System.out.println(Thread.currentThread().getName() + " locked lock1");
synchronized (lock2) {
System.out.println(Thread.currentThread().getName() + " locked lock2");
}
}
}
}
2)锁粒度:锁粒度指的是对共享资源加锁的范围。锁的粒度过大,会导致并发性能下降;锁的粒度过小,会增加锁管理的开销。
public class SynchronizedBlockExample {
private int count = 0;
// 只同步增量操作部分,避免同步无关代码
public void increment() {
synchronized (this) {
count++;
}
}
public int getCount() {
return count;
}
}
3)理解可重入性:synchronized 是可重入的,也就是说,同一个线程可以多次获得同一个锁而不会发生死锁
public class ReentrantLockExample {
// 如果一个线程在同步方法内部调用了另一个同步方法,它仍然能获取锁
public synchronized void methodA() {
System.out.println("Entering method A");
methodB(); // 调用另一个同步方法
}
public synchronized void methodB() {
System.out.println("Entering method B");
}
}
未完待续
很高兴与你相遇!如果你喜欢本文内容,记得关注哦
从MESA模型到锁升级:synchronized性能逆袭的底层逻辑的更多相关文章
- Synchronized锁性能优化偏向锁轻量级锁升级 多线程中篇(五)
不止一次的提到过,synchronized是Java内置的机制,是JVM层面的,而Lock则是接口,是JDK层面的 尽管最初synchronized的性能效率比较差,但是随着版本的升级,synchro ...
- 深入并发锁,解析Synchronized锁升级
这篇文章分为六个部分,不同特性的锁分类,并发锁的不同设计,Synchronized中的锁升级,ReentrantLock和ReadWriteLock的应用,帮助你梳理 Java 并发锁及相关的操作. ...
- synchronized与锁升级
1 为什么需要synchronized? 当一个共享资源有可能被多个线程同时访问并修改的时候,需要用锁来保证数据的正确性.请看下图: 线程A和线程B分别往同一个银行账户里面添加货币,A线程从内存中读取 ...
- 关于Synchronized的偏向锁,轻量级锁,重量级锁,锁升级过程,自旋优化,你该了解这些
前言 相信大部分开发人员,或多或少都看过或写过并发编程的代码.并发关键字除了Synchronized(如有不懂请移至传送门,关于Synchronized的偏向锁,轻量级锁,重量级锁,锁升级过程,自旋优 ...
- synchronized的锁升级/锁膨胀
偏向锁 偏向第一个拿到锁的线程. 即第一个拿到锁的线程,锁会在对象头 Mark Word 中通过 CAS 记录该线程 ID,该线程以后每次拿锁时都不需要进行 CAS(指轻量级锁). 如果该线程正在执行 ...
- 再谈synchronized锁升级
在图文详解Java对象内存布局这篇文章中,在研究对象头时我们了解了synchronized锁升级的过程,由于篇幅有限,对锁升级的过程介绍的比较简略,本文在上一篇的基础上,来详细研究一下锁升级的过程以及 ...
- 面试官:说一下Synchronized底层实现,锁升级的具体过程?
面试官:说一下Synchronized底层实现,锁升级的具体过程? 这是我去年7,8月份面试的时候被问的一个面试题,说实话被问到这个问题还是很意外的,感觉这个东西没啥用啊,直到后面被问了一波new O ...
- Synchronized锁升级原理与过程深入剖析
Synchronized锁升级原理与过程深入剖析 前言 在上篇文章深入学习Synchronized各种使用方法当中我们仔细介绍了在各种情况下该如何使用synchronized关键字.因为在我们写的程序 ...
- 简单的理解synchronized锁升级
前言 今天碰到一个synchronized锁升级的问题, 查了查, 发现一个帖子举例说明比较贴切, 特此转发, 如有问题, 欢迎讨论说明 转自: 木叶盒子 https://www.bilibili.c ...
- (转)DB2性能优化 – 如何通过调整锁参数优化锁升级
原文:http://blog.51cto.com/5063935/2074306 1.概念描述 所谓的锁升级(lock escalation),是数据库的一种作用机制,为了节约内存的开销, 其会将为数 ...
随机推荐
- vue3的defineProps使用外部类型
历史问题 这不是 Ts 的问题,而是 Vue 宏 defineProps 局限性, interface IProps { name: { type: String, default: "zh ...
- Eclipse WindowBuilder(SWT)插件安装及初次使用记录(萌新)
Eclipse WindowBuilder(SWT)插件安装及初次使用(萌新) 一.插件安装 (有VPN的挂VPN,服务器在外网更新下载比较慢) 1.首先更新到最新版本 点击Help,点击check ...
- Educational Codeforces Round 180 (Rated for Div. 2) C. Coloring Game
C – Coloring Game 思路:不难看出,当 Alice 选完三个数 a b c(其中 a ≤ b ≤ c)后,Bob 能选的只有两种情况: 选择 c,这样只用比较 a+b 和 c 的大小关 ...
- Codeforces Round #682 (Div. 2) ABC 题解
A. Specific Tastes of Andre 题意:构造一个长度为n且任意子串和整除其长度的序列. 思路:n个1就行. view code #include<iostream> ...
- DPDI:Kettle任务调度与管理的高效解决方案
DPDI:Kettle任务调度与管理的高效解决方案 引言 在企业大规模数据处理领域,Kettle(Pentaho Data Integration)作为一款广受欢迎的开源ETL工具,以其强大的数据处理 ...
- POLIR-Organization-大计(15、10、5年计划)-教材-如何分辨一本好书-十(二/三/四)五规划-引进版(原版/影印/编译版)-自研版:等级(得奖/国家级/省/地)+分级(研究生/本科/大中专/职业院校)
POLIR-Organization-大计(15.10.5年计划)-教材-十(二/三/四)五规划-等级(得奖/国家级/省/地)+分级(研究生/本科/大中专/职业院校) References: 北京大学 ...
- 从硬盘爆满到 GitHub 封号,一位前端开发者的开源历险记
前段时间,我结识了一位前端工程师「1024小神」.他的"战友"是一台 MacBook Air M3(8G+256GB),原本用来开发网页.小程序,绰绰有余. 然而,他的噩梦始于老板 ...
- win10专业版usb接口被禁用的问题
有深度系统的小伙伴,在win10专业版插入U盘,却没有任何反应,仔细检查发现USB接口被禁用了,不论插哪个USB口都无法正常使用的问题.那如何解除usb接口禁用呢?本文中,深度技术小编就来分享详细的处 ...
- SpringBoot启动原理(基于2.3.9.RELEASE版本)
目录 版本 总体上 main方法上的注解:@SpringBootApplication 源码 @SpringBootConfiguration @ComponentScan @EnableAutoCo ...
- [ROI 2023] 峰值 (Day 1)
\(\mathbf{Part. -1}\) 翻译自 ROI 2023 D1T3. 如果对于所有 \(1 \le j < i\),都有 \(a_j < a_i\),则称 \(a_i\) 为峰 ...