Java 中的 synchronized
关键字深度解析
synchronized
是 Java 中最核心的线程同步机制 ,用于解决多线程环境下的数据竞争 和可见性 问题。以下是全面解析:
一、synchronized
的三大用法
1. 同步实例方法
public synchronized void increment ( ) {
count++ ;
}
2. 同步静态方法
public static synchronized void staticMethod ( ) {
}
3. 同步代码块
public void doSomething ( ) {
synchronized ( lockObject) {
}
}
二、锁的底层实现
1. 对象头与 Monitor 结构
无锁
偏向锁
轻量级锁
重量级锁
Java对象头
Mark Word
锁状态标识
01
01
00
10
GC信息/哈希值
Klass Pointer
2. 锁升级过程(JDK 6+ 优化)
多个线程轻度竞争
竞争加剧
释放锁
无锁
偏向锁
轻量级锁
重量级锁
锁状态对比:
锁类型 优点 适用场景 偏向锁 加解锁开销最小 单线程访问 轻量级锁 避免OS用户态切换 低竞争,同步块执行快 重量级锁 解决激烈竞争 高并发访问
三、内存可见性保证
Happens-Before 关系
synchronized ( lock) {
x = 42 ;
}
synchronized ( lock) {
System . out. println ( x) ;
}
解锁操作 happens-before 后续对同一锁的加锁操作 确保修改对其他线程可见(避免缓存不一致)
四、Synchronized 的替代方案
Java 中的 synchronized
关键字深度解析
synchronized
是 Java 中最核心的线程同步机制 ,用于解决多线程环境下的数据竞争 和可见性 问题。以下是全面解析:
一、synchronized
的三大用法
1. 同步实例方法
public synchronized void increment ( ) {
count++ ;
}
2. 同步静态方法
public static synchronized void staticMethod ( ) {
}
3. 同步代码块
public void doSomething ( ) {
synchronized ( lockObject) {
}
}
二、锁的底层实现
1. 对象头与 Monitor 结构
无锁
偏向锁
轻量级锁
重量级锁
Java对象头
Mark Word
锁状态标识
01
01
00
10
GC信息/哈希值
Klass Pointer
2. 锁升级过程(JDK 6+ 优化)
多个线程轻度竞争
竞争加剧
释放锁
无锁
偏向锁
轻量级锁
重量级锁
锁状态对比:
锁类型 优点 适用场景 偏向锁 加解锁开销最小 单线程访问 轻量级锁 避免OS用户态切换 低竞争,同步块执行快 重量级锁 解决激烈竞争 高并发访问
三、内存可见性保证
Happens-Before 关系
synchronized ( lock) {
x = 42 ;
}
synchronized ( lock) {
System . out. println ( x) ;
}
解锁操作 happens-before 后续对同一锁的加锁操作 确保修改对其他线程可见(避免缓存不一致)
四、Synchronized 的替代方案
synchronized
vs ReentrantLock
特性 synchronized
ReentrantLock
锁获取方式 JVM自动管理 需手动lock/unlock 公平性 非公平(默认) 支持公平/非公平 中断响应 ❌ 不支持 ✅ 支持lockInterruptibly 超时机制 ❌ 不支持 ✅ 支持tryLock(timeout) 条件变量 只能使用1个等待队列 可创建多个Condition 性能优化 JDK6+自适应性锁升级 复杂场景表现更优
五、正确使用实践
1. 避免死锁的四项原则
Thread1 : synchronized ( A ) { synchronized ( B ) { . . . } }
Thread2 : synchronized ( B ) { synchronized ( A ) { . . . } }
解决方案:
固定顺序获取锁 if ( A . hashCode ( ) > B . hashCode ( ) ) {
synchronized ( A ) { synchronized ( B ) { . . . } }
} else {
synchronized ( B ) { synchronized ( A ) { . . . } }
}
使用tryLock超时机制 (仅ReentrantLock支持)
2. 锁粒度优化
public synchronized void process ( ) {
readFile ( ) ;
compute ( ) ;
updateDB ( ) ;
}
public void optimizedProcess ( ) {
readFile ( ) ;
synchronized ( this ) {
compute ( ) ;
}
updateDB ( ) ;
}
3. 锁对象选择
private final Object lock1 = new Object ( ) ;
private final Object lock2 = new Object ( ) ;
void methodA ( ) {
synchronized ( lock1) { }
}
void methodB ( ) {
synchronized ( lock2) { }
}
六、性能注意事项
基准测试结果示例
场景 吞吐量 (ops/ms)无锁 350,000 偏向锁 320,000 轻量级锁 180,000 重量级锁 50,000 ReentrantLock (非公平) 80,000
优化建议:
减少同步块执行时间 :避免在同步块内执行IO/复杂计算分拆锁 :使用多个锁减少竞争读写分离 :读写锁替代全同步(适用读多写少)
七、常见问题解决
1. 对象锁 vs 类锁
class Example {
public void instanceMethod ( ) {
synchronized ( this ) { . . . }
}
public static void classMethod ( ) {
synchronized ( Example . class ) { . . . }
}
}
2. synchronized与继承
class Parent {
public synchronized void method ( ) { . . . }
}
class Child extends Parent {
@Override
public synchronized void method ( ) {
}
}
八、高级技巧(JDK 14+)
轻量级锁性能优化
@sun.misc.Contended
class Counter {
private volatile long value;
}
总结指南
使用流程图
graph TD
A[需要同步共享资源吗?] --> B{竞争强度}
B -->|低竞争| C[优先用 synchronized]
B -->|高竞争| D{需高级功能?}
D -->|需条件变量/公平性| E[用 ReentrantLock]
D -->|需读写分离| F[用 ReadWriteLock]
C --> G[选择锁对象]
G -->|普通资源| H[用实例锁]
G -->|类级别资源| I[用类锁]
G -->|细粒度控制| J[用专用锁对象]
黄金准则:
锁对象不可变 :确保同步有效性避免锁方法块过大 :减小临界区优先用私有锁 :private final Object lock = new Object()
考虑并发容器 :优先用 ConcurrentHashMap
等
📌 最佳实践 :在大多数业务场景下,优先使用简单的 synchronized
,仅在需要其缺乏的高级功能时使用更复杂的 Lock
实现。
synchronized
vs ReentrantLock
特性 synchronized
ReentrantLock
锁获取方式 JVM自动管理 需手动lock/unlock 公平性 非公平(默认) 支持公平/非公平 中断响应 ❌ 不支持 ✅ 支持lockInterruptibly 超时机制 ❌ 不支持 ✅ 支持tryLock(timeout) 条件变量 只能使用1个等待队列 可创建多个Condition 性能优化 JDK6+自适应性锁升级 复杂场景表现更优
五、正确使用实践
1. 避免死锁的四项原则
Thread1 : synchronized ( A ) { synchronized ( B ) { . . . } }
Thread2 : synchronized ( B ) { synchronized ( A ) { . . . } }
解决方案:
固定顺序获取锁 if ( A . hashCode ( ) > B . hashCode ( ) ) {
synchronized ( A ) { synchronized ( B ) { . . . } }
} else {
synchronized ( B ) { synchronized ( A ) { . . . } }
}
使用tryLock超时机制 (仅ReentrantLock支持)
2. 锁粒度优化
public synchronized void process ( ) {
readFile ( ) ;
compute ( ) ;
updateDB ( ) ;
}
public void optimizedProcess ( ) {
readFile ( ) ;
synchronized ( this ) {
compute ( ) ;
}
updateDB ( ) ;
}
3. 锁对象选择
private final Object lock1 = new Object ( ) ;
private final Object lock2 = new Object ( ) ;
void methodA ( ) {
synchronized ( lock1) { }
}
void methodB ( ) {
synchronized ( lock2) { }
}
六、性能注意事项
基准测试结果示例
场景 吞吐量 (ops/ms)无锁 350,000 偏向锁 320,000 轻量级锁 180,000 重量级锁 50,000 ReentrantLock (非公平) 80,000
优化建议:
减少同步块执行时间 :避免在同步块内执行IO/复杂计算分拆锁 :使用多个锁减少竞争读写分离 :读写锁替代全同步(适用读多写少)
七、常见问题解决
1. 对象锁 vs 类锁
class Example {
public void instanceMethod ( ) {
synchronized ( this ) { . . . }
}
public static void classMethod ( ) {
synchronized ( Example . class ) { . . . }
}
}
2. synchronized与继承
class Parent {
public synchronized void method ( ) { . . . }
}
class Child extends Parent {
@Override
public synchronized void method ( ) {
}
}
八、高级技巧(JDK 14+)
轻量级锁性能优化
@sun.misc.Contended
class Counter {
private volatile long value;
}
总结指南
使用流程图
graph TD
A[需要同步共享资源吗?] --> B{竞争强度}
B -->|低竞争| C[优先用 synchronized]
B -->|高竞争| D{需高级功能?}
D -->|需条件变量/公平性| E[用 ReentrantLock]
D -->|需读写分离| F[用 ReadWriteLock]
C --> G[选择锁对象]
G -->|普通资源| H[用实例锁]
G -->|类级别资源| I[用类锁]
G -->|细粒度控制| J[用专用锁对象]
黄金准则:
锁对象不可变 :确保同步有效性避免锁方法块过大 :减小临界区优先用私有锁 :private final Object lock = new Object()
考虑并发容器 :优先用 ConcurrentHashMap
等
📌 最佳实践 :在大多数业务场景下,优先使用简单的 synchronized
,仅在需要其缺乏的高级功能时使用更复杂的 Lock
实现。