一句话总结
1. 机制:synchronized是Java关键字,基于JVM实现自动锁管理;Lock是接口,需手动加锁解锁。
2. 灵活性:Lock支持非阻塞尝试锁(tryLock)、可中断锁、超时锁,synchronized无法实现。
3. 公平性:Lock可设置公平/非公平策略,synchronized仅支持非公平锁。
4. 条件变量:Lock可通过Condition绑定多个条件,synchronized仅一个等待队列。
5. 性能:高并发时Lock更高效,synchronized优化后差距缩小,但Lock需显式释放避免死锁。
详细解析
synchronized和Lock(以ReentrantLock为例)都是 Java 中用于实现线程同步的机制,但它们在设计、功能和使用方式上有显著区别。
以下是核心差异的对比:
1. 类型与实现层面
- synchronized:Java 语言内置的关键字,基于 JVM 实现的 内置锁(Monitor Lock)。其底层通过对象的监视器(Monitor)实现,依赖 JVM 的字节码指令(monitorenter和monitorexit)来管理锁的获取和释放。
- Lock:java.util.concurrent.locks.Lock接口的实现类(如ReentrantLock),属于 API 层面的显式锁。通过 Java 代码实现锁逻辑(如使用CAS操作和AQS队列同步器),更灵活但需要手动管理。
2. 锁的获取与释放方式
- synchronized:隐式获取和释放锁。
进入同步块(或方法)时自动获取锁,退出同步块(正常退出或异常退出)时自动释放锁(由 JVM 保证)。
- Lock:显式获取和释放锁。
需手动调用lock()或tryLock()获取锁,并在finally块中调用unlock()释放锁(否则可能导致死锁)。
3. 锁的特性差异
| 特性 | synchronized | Lock(如 ReentrantLock) |
|---|---|---|
| 可中断性 | 不可中断(除非抛出异常)。 | 支持可中断:通过lockInterruptibly()方法,等待锁时可响应中断。 |
| 超时获取锁 | 不支持(阻塞直到获取锁)。 | 支持超时:通过tryLock(long timeout, TimeUnit unit),超时未获取则放弃。 |
| 公平性 | 默认非公平锁(线程获取锁的顺序与等待顺序无关)。 | 可通过构造函数ReentrantLock(boolean fair)指定公平锁(按等待队列顺序分配锁)。 |
| 条件变量(Condition) | 仅支持单一等待队列(通过wait()/notify())。 | 支持多个Condition对象(如newCondition()),实现更细粒度的等待/通知(如生产者-消费者模型中区分“读”和“写”条件)。 |
4. 性能与优化
- synchronized:早期版本(JDK 1.6 前)性能较差(依赖操作系统互斥量,线程阻塞/唤醒开销大)。
JDK 1.6 后引入 锁升级优化(偏向锁→轻量级锁→重量级锁),减少无竞争或低竞争场景下的性能损耗,与Lock性能接近。
- Lock:通过CAS(无锁操作)和AQS(队列同步器)实现,在高竞争或复杂场景(如需要可中断、超时)中更灵活,但需手动管理锁。
5. 使用场景
- synchronized:适合简单同步需求(如保护短代码块、避免手动释放锁),代码更简洁。
例如:保护非静态成员变量的同步方法、短时间的同步代码块。
- Lock:适合复杂同步需求(如需要可中断、超时、公平锁或多条件变量)。
例如:需要精确控制锁释放时机、实现生产者-消费者模型中的多条件等待(Condition.await()/signal())。
总结
| 维度 | synchronized | Lock(如 ReentrantLock) |
|---|---|---|
| 类型 | JVM 内置关键字 | JUC 包的 API 接口实现 |
| 锁管理 | 隐式(自动获取/释放) | 显式(手动lock()/unlock()) |
| 灵活性 | 低(固定语义) | 高(可中断、超时、公平锁、多条件) |
| 异常安全 | 自动释放(异常时安全) | 需finally块保证释放 |
| 适用场景 | 简单同步、短代码块 | 复杂同步、高竞争、需要细粒度控制 |
以上为个人经验,希望能给大家一个参考,也希望大家多多支持。










