Java 中的 synchronized

Java 中的 synchronized 关键字深度解析

synchronized 是 Java 中最核心的线程同步机制,用于解决多线程环境下的数据竞争可见性问题。以下是全面解析:


一、synchronized 的三大用法

1. 同步实例方法

public synchronized void increment() {
    count++; // 锁对象是当前实例(this)
}

2. 同步静态方法

public static synchronized void staticMethod() {
    // 锁对象是类的Class对象(如 MyClass.class)
}

3. 同步代码块

public void doSomething() {
    // 非同步代码
    
    synchronized(lockObject) { // 显式指定锁对象
        // 需要同步的代码
    }
}

二、锁的底层实现

1. 对象头与 Monitor 结构

无锁
偏向锁
轻量级锁
重量级锁
Java对象头
Mark Word
锁状态标识
01
01
00
10
GC信息/哈希值
Klass Pointer

2. 锁升级过程(JDK 6+ 优化)

多个线程轻度竞争
竞争加剧
释放锁
无锁
偏向锁
轻量级锁
重量级锁
锁状态对比:
锁类型优点适用场景
偏向锁加解锁开销最小单线程访问
轻量级锁避免OS用户态切换低竞争,同步块执行快
重量级锁解决激烈竞争高并发访问

三、内存可见性保证

Happens-Before 关系

// 线程A
synchronized(lock) {
    x = 42;  // 写入操作
}

// 线程B
synchronized(lock) {
    System.out.println(x); // 保证看到42
}
  • 解锁操作 happens-before 后续对同一锁的加锁操作
  • 确保修改对其他线程可见(避免缓存不一致)

四、Synchronized 的替代方案

Java 中的 synchronized 关键字深度解析

synchronized 是 Java 中最核心的线程同步机制,用于解决多线程环境下的数据竞争可见性问题。以下是全面解析:


一、synchronized 的三大用法

1. 同步实例方法

public synchronized void increment() {
    count++; // 锁对象是当前实例(this)
}

2. 同步静态方法

public static synchronized void staticMethod() {
    // 锁对象是类的Class对象(如 MyClass.class)
}

3. 同步代码块

public void doSomething() {
    // 非同步代码
    
    synchronized(lockObject) { // 显式指定锁对象
        // 需要同步的代码
    }
}

二、锁的底层实现

1. 对象头与 Monitor 结构

无锁
偏向锁
轻量级锁
重量级锁
Java对象头
Mark Word
锁状态标识
01
01
00
10
GC信息/哈希值
Klass Pointer

2. 锁升级过程(JDK 6+ 优化)

多个线程轻度竞争
竞争加剧
释放锁
无锁
偏向锁
轻量级锁
重量级锁
锁状态对比:
锁类型优点适用场景
偏向锁加解锁开销最小单线程访问
轻量级锁避免OS用户态切换低竞争,同步块执行快
重量级锁解决激烈竞争高并发访问

三、内存可见性保证

Happens-Before 关系

// 线程A
synchronized(lock) {
    x = 42;  // 写入操作
}

// 线程B
synchronized(lock) {
    System.out.println(x); // 保证看到42
}
  • 解锁操作 happens-before 后续对同一锁的加锁操作
  • 确保修改对其他线程可见(避免缓存不一致)

四、Synchronized 的替代方案

synchronized vs ReentrantLock

特性synchronizedReentrantLock
锁获取方式JVM自动管理需手动lock/unlock
公平性非公平(默认)支持公平/非公平
中断响应❌ 不支持✅ 支持lockInterruptibly
超时机制❌ 不支持✅ 支持tryLock(timeout)
条件变量只能使用1个等待队列可创建多个Condition
性能优化JDK6+自适应性锁升级复杂场景表现更优

五、正确使用实践

1. 避免死锁的四项原则

// 错误示例:交叉锁死锁
Thread1: synchronized(A) { synchronized(B) { ... } }
Thread2: synchronized(B) { synchronized(A) { ... } }
解决方案:
  1. 固定顺序获取锁
    if (A.hashCode() > B.hashCode()) {
        synchronized(A) { synchronized(B) { ... } }
    } else {
        synchronized(B) { synchronized(A) { ... } }
    }
    
  2. 使用tryLock超时机制(仅ReentrantLock支持)

2. 锁粒度优化

// 错误:锁范围过大
public synchronized void process() {
    readFile(); // 耗时IO(不必要同步)
    compute();  // 计算密集型操作
    updateDB(); // 耗时DB操作
}

// 正确:最小化同步范围
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) { /* 操作资源1 */ }
}

void methodB() {
    synchronized(lock2) { /* 操作资源2 */ }
}

六、性能注意事项

基准测试结果示例

场景吞吐量 (ops/ms)
无锁350,000
偏向锁320,000
轻量级锁180,000
重量级锁50,000
ReentrantLock (非公平)80,000

优化建议:

  1. 减少同步块执行时间:避免在同步块内执行IO/复杂计算
  2. 分拆锁:使用多个锁减少竞争
  3. 读写分离:读写锁替代全同步(适用读多写少)

七、常见问题解决

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() {
        // 子类方法仍保持同步(锁对象是Child实例)
    }
}

八、高级技巧(JDK 14+)

轻量级锁性能优化

// 使用Contended避免伪共享
@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[用专用锁对象]

黄金准则:

  1. 锁对象不可变:确保同步有效性
  2. 避免锁方法块过大:减小临界区
  3. 优先用私有锁private final Object lock = new Object()
  4. 考虑并发容器:优先用 ConcurrentHashMap

📌 最佳实践:在大多数业务场景下,优先使用简单的 synchronized,仅在需要其缺乏的高级功能时使用更复杂的 Lock 实现。

synchronized vs ReentrantLock

特性synchronizedReentrantLock
锁获取方式JVM自动管理需手动lock/unlock
公平性非公平(默认)支持公平/非公平
中断响应❌ 不支持✅ 支持lockInterruptibly
超时机制❌ 不支持✅ 支持tryLock(timeout)
条件变量只能使用1个等待队列可创建多个Condition
性能优化JDK6+自适应性锁升级复杂场景表现更优

五、正确使用实践

1. 避免死锁的四项原则

// 错误示例:交叉锁死锁
Thread1: synchronized(A) { synchronized(B) { ... } }
Thread2: synchronized(B) { synchronized(A) { ... } }
解决方案:
  1. 固定顺序获取锁
    if (A.hashCode() > B.hashCode()) {
        synchronized(A) { synchronized(B) { ... } }
    } else {
        synchronized(B) { synchronized(A) { ... } }
    }
    
  2. 使用tryLock超时机制(仅ReentrantLock支持)

2. 锁粒度优化

// 错误:锁范围过大
public synchronized void process() {
    readFile(); // 耗时IO(不必要同步)
    compute();  // 计算密集型操作
    updateDB(); // 耗时DB操作
}

// 正确:最小化同步范围
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) { /* 操作资源1 */ }
}

void methodB() {
    synchronized(lock2) { /* 操作资源2 */ }
}

六、性能注意事项

基准测试结果示例

场景吞吐量 (ops/ms)
无锁350,000
偏向锁320,000
轻量级锁180,000
重量级锁50,000
ReentrantLock (非公平)80,000

优化建议:

  1. 减少同步块执行时间:避免在同步块内执行IO/复杂计算
  2. 分拆锁:使用多个锁减少竞争
  3. 读写分离:读写锁替代全同步(适用读多写少)

七、常见问题解决

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() {
        // 子类方法仍保持同步(锁对象是Child实例)
    }
}

八、高级技巧(JDK 14+)

轻量级锁性能优化

// 使用Contended避免伪共享
@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[用专用锁对象]

黄金准则:

  1. 锁对象不可变:确保同步有效性
  2. 避免锁方法块过大:减小临界区
  3. 优先用私有锁private final Object lock = new Object()
  4. 考虑并发容器:优先用 ConcurrentHashMap

📌 最佳实践:在大多数业务场景下,优先使用简单的 synchronized,仅在需要其缺乏的高级功能时使用更复杂的 Lock 实现。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值