Java 中 Lock 和 synchronized 的区别是什么?

1. 基本特性对比

特性synchronizedLock (如 ReentrantLock)
实现方式Java 关键字,JVM 内置支持Java 接口,需要显式创建对象
锁获取自动获取和释放锁需要显式调用 lock() 和 unlock()
灵活性较低,结构固定较高,可控制锁的获取时机
性能早期版本较差,JDK 1.6 后大幅优化通常性能更好,尤其在高竞争场景

2. 主要区别详解

2.1 使用方式

// synchronized 使用
public synchronized void syncMethod() {
    // 方法体
}

public void syncBlock() {
    synchronized(this) {
        // 同步代码块
    }
}

// Lock 使用
private final Lock lock = new ReentrantLock();

public void lockMethod() {
    lock.lock();
    try {
        // 临界区代码
    } finally {
        lock.unlock();  // 确保锁释放
    }
}

2.2 可中断性

// synchronized 不可中断
synchronized void syncMethod() {
    // 如果线程在等待锁,无法被中断
}

// Lock 支持可中断获取
lock.lockInterruptibly();  // 等待时可响应中断
try {
    // ...
} finally {
    lock.unlock();
}

2.3 尝试获取锁

// synchronized 只能阻塞等待
synchronized(obj) {
    // 拿不到锁就一直等
}

// Lock 可以尝试获取
if (lock.tryLock()) {  // 立即返回结果
    try {
        // 获取锁成功
    } finally {
        lock.unlock();
    }
} else {
    // 获取锁失败,执行其他逻辑
}

// 带超时的尝试
if (lock.tryLock(3, TimeUnit.SECONDS)) {
    // ...
}

2.4 公平性

// synchronized 是非公平锁
synchronized(obj) { ... }

// Lock 可以创建公平锁
Lock fairLock = new ReentrantLock(true);  // true 表示公平锁

2.5 条件变量

// synchronized 配合 wait/notify
synchronized(lockObj) {
    while (!condition) {
        lockObj.wait();
    }
    // 执行业务
    lockObj.notifyAll();
}

// Lock 使用 Condition
private final Condition condition = lock.newCondition();

lock.lock();
try {
    while (!conditionMet) {
        condition.await();  // 等待条件
    }
    // 执行业务
    condition.signal();  // 唤醒一个等待线程
} finally {
    lock.unlock();
}

3. 选择建议

使用 synchronized 的场景:

  • 简单的同步需求
  • 代码量少,希望减少复杂性
  • 锁持有时间短,竞争不激烈
  • 需要自动管理锁的获取和释放

使用 Lock 的场景:

  • 需要可中断的锁获取
  • 需要尝试获取锁(非阻塞或超时等待)
  • 需要公平锁策略
  • 需要多个条件变量
  • 锁需要在多个方法间传递
  • 需要更细粒度的锁控制

4. 性能考虑

  • JDK 1.5 之前:synchronized 性能较差
  • JDK 1.6 及以后:synchronized 引入了锁升级机制(偏向锁→轻量级锁→重量级锁),性能大幅提升
  • 高竞争场景:Lock 通常表现更好
  • 低竞争场景:synchronized 性能足够且更简单

5. 注意事项

  1. Lock 必须手动释放:必须在 finally 块中调用 unlock()
  2. synchronized 的锁范围:可以修饰方法或代码块
  3. 可重入性:两者都支持可重入
  4. 死锁风险:Lock 使用不当更容易导致死锁

总结

synchronized是 Java 内置的简单同步机制,适合大多数基本同步需求。Lock接口提供了更丰富的功能和控制能力,适合复杂的同步场景。选择时需根据具体需求、性能要求和代码复杂度综合考虑。


作 者:南烛
链 接:https://www.itnotes.top/archives/895
来 源:IT笔记
文章版权归作者所有,转载请注明出处!


上一篇
下一篇