一句话总结
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块保证释放 |
| 适用场景 | 简单同步、短代码块 | 复杂同步、高竞争、需要细粒度控制 |
以上为个人经验,希望能给大家一个参考,也希望大家多多支持代码网。
发表评论