synchronized 与 lock 的区别
synchronized 和 lock 的用法区别
- synchronized(隐式锁):在需要同步的对象中加入此控制,
synchronized可以加在方法上,也可以加在特定代码块中,括号中表示需要锁的对象。 - lock(显示锁):需要显示指定起始位置和终止位置。一般使用
ReentrantLock类做为锁,多个线程中必须要使用一个ReentrantLock类做为对象才能保证锁的生效。且在加锁和解锁处需要通过lock()和unlock()显示指出。所以一般会在finally块中写unlock()以防死锁。
synchronized 和 lock 性能区别
synchronized是托管给 JVM 执行的,而lock是 Java 写的控制锁的代码。在 JDK 1.5 中,synchronize是性能低效的。因为这是一个重量级操作,需要调用操作接口,导致有可能加锁消耗的系统时间比加锁以外的操作还多。相比之下使用 Java 提供的Lock对象,性能更高一些。但是到了 JDK 1.6,发生了变化。synchronize在语义上很清晰,可以进行很多优化,有适应自旋,锁消除,锁粗化,轻量级锁,偏向锁等等。导致在 JDK 1.6 上synchronize的性能并不比Lock差。
synchronized 和 lock 机制区别
synchronized原始采用的是 CPU 悲观锁机制,即线程获得的是独占锁。独占锁意味着其 他线程只能依靠阻塞来等待线程释放锁。Lock用的是乐观锁方式。所谓乐观锁就是,每次不加锁而是假设没有冲突而去完成某项操作,如果因为冲突失败就重试,直到成功为止。乐观锁实现的机制就是 CAS 操作(Compare and Swap)。
用法/性能/机制三段对比是标准答法,但其中”Lock 是乐观锁”的说法需要修正,且还缺一块最重要的内容:功能差异清单与选型决策。本文修正+补全。
修正:Lock 不是乐观锁
ReentrantLock 默认实现与 synchronized 一样是悲观锁(竞争者 park 阻塞等待)。”乐观锁=CAS”描述的是 AQS 内部获取锁的动作用 CAS 改 state,而非 Lock 的并发哲学。把”Lock=乐观锁”当结论背会在面试追问下崩塌。准确表述:两者都是悲观互斥锁,差异在功能面与实现层。
功能差异清单(选型的真正依据)
| 能力 | synchronized | ReentrantLock |
|---|---|---|
| 释放方式 | 自动(出块/异常均释放) | 手动 unlock,必须 finally |
| 等待可中断 | 不可 | lockInterruptibly |
| 超时可放弃 | 不可 | tryLock(timeout) |
| 公平性 | 非公平 | 构造参数可选公平 |
| 条件队列 | 单一 wait/notify | 多个 Condition 分组唤醒 |
| 锁绑定 | JVM 关键字 | API 对象,可传递/组合 |
| 查询能力 | 无 | isLocked/getQueueLength 等监控 API |
三个”只有 Lock 能”的场景:
- 限时尝试:tryLock(50ms) 失败走降级路径(缓存/排队),避免请求堆积等锁
- 可中断等待:用户取消请求时中断正在等锁的线程,synchronized 做不到
- 多条件精确唤醒:生产者-消费者用 notFull/notEmpty 两个 Condition,避免 notifyAll 全量唤醒的浪费
性能对比的现代结论
JDK6 之后 synchronized 引入偏向锁/轻量级锁/自适应自旋/锁消除/锁粗化,无竞争与低竞争场景开销极小;ReentrantLock 基于 AQS,CAS 改 state + park 阻塞,两者在多数基准下差距在噪声范围内。**”性能”不再是选型理由,功能需求才是**。JDK15 起偏向锁默认禁用(撤销成本高),但轻量级路径仍在,结论不变。
使用规范
synchronized:
- 锁对象选私有 final 对象或 this,避免锁字符串/公开对象被外部干扰
- 临界区最小化:IO、远程调用、sleep 移出锁外
ReentrantLock:
lock.lock(); |
- lock() 必须在 try 之外(lock 本身抛异常则不应 unlock)
- 公平锁吞吐低于非公平锁(减少切换机会),仅在有饥饿要求时启用
- Condition 的 await 也要放在循环中检查条件(防虚假唤醒),与 wait 规范相同
选型决策树
- 只需互斥、无特殊需求 → synchronized(不会忘释放,JVM 持续优化)
- 需要超时/中断/公平/多条件之一 → ReentrantLock
- 只需单变量原子复合 → atomic 包(比两者都轻)
- 读多写极少 → ReadWriteLock / StampedLock(注意 StampedLock 不可重入、不支持条件变量)
synchronized 与 Lock 的区别,历史性能叙事已过期,今天的答案在功能面:synchronized 是”够用且安全”的默认,Lock 是”需要控制等待行为时”的升级。把选型建立在功能需求而非性能传说上,这个题目就答到了现代版本。
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来源 非鱼小站!
评论
WalineDisqus











