目录
synchronized是Java中实现线程同步的关键字,它提供了内置的锁机制,可以保证同一时刻只有一个线程能够访问被同步的代码块或方法。下面我将从多个层面详细解析synchronized的底层实现原理。
1. 基本使用方式
synchronized有三种基本使用方式:
-
同步实例方法:锁是当前实例对象
public synchronized void method() { // ... } -
同步静态方法:锁是当前类的Class对象
public static synchronized void staticMethod() { // ... } -
同步代码块:锁是括号里配置的对象
public void method() { synchronized(obj) { // ... } }
2. 底层实现原理
synchronized的底层实现主要依赖于JVM中的Monitor(监视器锁)机制,具体实现涉及以下几个方面:
2.1 Java对象头
在JVM中,每个对象都有一个对象头,其中包含两部分信息:
-
Mark Word:存储对象自身的运行时数据
-
Klass Pointer:指向对象所属类的元数据的指针
synchronized使用的锁信息就存储在Mark Word中。Mark Word在不同状态下会存储不同的内容:
| 锁状态 | 存储内容 | 标志位 |
|---|---|---|
| 无锁 | 对象的hashCode、分代年龄等 | 01 |
| 偏向锁 | 偏向线程ID、偏向时间戳、分代年龄等 | 01 |
| 轻量级锁 | 指向栈中锁记录的指针 | 00 |
| 重量级锁 | 指向互斥量(重量级锁)的指针 | 10 |
| GC标记 | 空 | 11 |
2.2 Monitor机制
Monitor是JVM提供的一种同步机制,每个Java对象都可以关联一个Monitor对象。Monitor的主要组成部分包括:
-
Owner:持有该Monitor的线程
-
EntryList:处于阻塞状态的线程队列
-
WaitSet:处于等待状态的线程队列(调用了wait()方法的线程)
当线程执行到synchronized代码块时:
-
尝试获取对象的Monitor
-
如果获取成功,成为Monitor的Owner
-
如果获取失败,进入EntryList等待
2.3 锁升级过程
为了减少锁操作的开销,JVM实现了锁的升级机制:
-
无锁状态:初始状态
-
偏向锁:当第一个线程访问同步块时,会进入偏向模式
-
在Mark Word中记录线程ID
-
后续该线程进入同步块时不需要任何同步操作
-
-
轻量级锁:当有第二个线程尝试获取锁时,偏向锁升级为轻量级锁
-
线程通过CAS操作竞争锁
-
适用于线程交替执行的场景
-
-
重量级锁:当竞争激烈时(自旋获取锁失败),轻量级锁升级为重量级锁
-
线程会被阻塞,进入内核态等待
-
性能开销最大
-
3. 字节码层面分析
从字节码角度看,synchronized的实现:
-
同步代码块使用
monitorenter和monitorexit指令 -
同步方法使用
ACC_SYNCHRONIZED标志
3.1 同步代码块
public void syncBlock() {
synchronized(this) {
System.out.println("sync block");
}
}
对应的字节码:
public void syncBlock();
Code:
0: aload_0
1: dup
2: astore_1
3: monitorenter // 进入同步块
4: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
7: ldc #3 // String sync block
9: invokevirtual #4 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
12: aload_1
13: monitorexit // 正常退出同步块
14: goto 22
17: astore_2
18: aload_1
19: monitorexit // 异常退出同步块
20: aload_2
21: athrow
22: return
3.2 同步方法
public synchronized void syncMethod() {
System.out.println("sync method");
}
对应的字节码:
public synchronized void syncMethod();
flags: ACC_PUBLIC, ACC_SYNCHRONIZED
Code:
0: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
3: ldc #5 // String sync method
5: invokevirtual #4 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
8: return
4. 锁优化技术
JVM对synchronized进行了多种优化:
-
自旋锁:竞争锁失败的线程不立即阻塞,而是执行忙循环(自旋)
-
锁消除:JIT编译器通过逃逸分析消除不必要的锁
-
锁粗化:将连续的多个锁操作合并为一个更大的锁操作
-
适应性自旋:根据历史情况动态调整自旋时间
5. 与其他锁的比较
| 特性 | synchronized | ReentrantLock |
|---|---|---|
| 实现方式 | JVM内置 | JDK实现 |
| 获取锁方式 | 自动获取释放 | 手动lock/unlock |
| 锁类型 | 非公平锁 | 可公平/非公平 |
| 条件变量 | 单一 | 多个 |
| 中断响应 | 不支持 | 支持 |
| 性能 | 优化后相当 | 相当 |
6. 使用建议
-
优先使用
synchronized,除非需要高级功能 -
尽量缩小同步代码块的范围
-
避免在同步块中执行耗时操作
-
注意死锁问题,避免嵌套获取多个锁
<<Java高并发实战:原理、源码与性能调优>>
<<Java集合框架深度解析:从源码到实战应用>>
📣 互动邀请
如果本文对您有帮助,请不要吝啬您的支持:
👍 点赞 - 您的点赞是我持续创作的最大动力!
🔔 关注 - 获取更多Java核心技术解析和实战技巧
💬 评论 - 有任何问题或建议欢迎留言讨论
🚀 下期预告
接下来我将继续深入讲解:
《ReentrantLock底层原理详解:从源码到实战全面解析》
1271

被折叠的 条评论
为什么被折叠?



