Java中synchronized 和 Lock 有什么区别?

synchronized是关键字,自动管理锁,使用简单但功能有限;Lock是接口,需手动加锁解锁,支持公平锁、可中断、超时获取等高级功能,灵活性高但需注意异常处理与释放。

在Java中,synchronizedLock 都用于实现线程同步,保证多线程环境下数据的一致性,但它们在使用方式、功能和灵活性上有明显区别。

1. 语法层面的差异

synchronized 是Java语言的关键字,JVM层面支持,使用简单。可以修饰方法或代码块,进入同步区域时自动获取锁,退出时自动释放。

示例:
public synchronized void method() {
    // 同步方法
}

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

Lock 是一个接口(如 ReentrantLock),需要手动创建实例,并显式调用 lock() 和 unlock() 方法控制锁的获取与释放。

示例:
Lock lock = new ReentrantLock();
lock.lock();
try {
    // 临界区
} finally {
    lock.unlock(); // 必须在finally中释放
}

2. 锁的获取与释放控制

synchronized 的锁是自动管理的,线程退出同步代码块或方法后自动释放,即使发生异常也不会导致死锁。

Lock 必须手动释放,如果不调用 unlock(),可能导致其他线程永久等待,造成死锁。因此通常将 unlock() 放在 finally 块中确保执行。

Lock 提供了更灵活的控制方式,比如尝试获取锁(tryLock)、带超时获取锁(tryLock(long, TimeUnit))、可中断地获取锁(lockInterruptibly)等。

  • tryLock():立即返回是否获取成功,适合避免阻塞
  • tryLock(5, TimeUnit.SECONDS):在指定时间内尝试获取锁
  • lockInterruptibly():允许在等待锁的过程中响应中断

3. 公平性支持

synchronized 不支持公平锁,多个线程竞争时无法保证先到先得。

ReentrantLock 可以通过构造函数选择是否使用公平锁:

Lock lock = new ReentrantLock(true); // 公平锁

公平锁能减少线程饥饿问题,但性能较低,因为需要维护等待队列。

4. 性能与适用场景

早期版本中 synchronized 性能较差,但自从JDK 1.6引入偏向锁、轻量级锁等优化后,性能已大幅提升,在大多数场景下与 Lock 差别不大。

如果只是简单的互斥操作,优先使用 synchronized,代码更简洁、安全。

如果需要尝试非阻塞获取锁、超时获取、可中断、条件等待(Condition)等高级功能,则使用 Lock 更合适。

基本上就这些。synchronized 简单可靠,适合大部分同步需求;Lock 功能强大,适合复杂控制场景,但使用要更小心。不复杂但容易忽略的是:手动释放锁和异常处理。