synchronized 与 Lock 的区别
·
一、底层实现层面
- synchronized JVM 底层基于对象 Monitor 监视器,依赖操作系统内核互斥锁;JDK1.6 做锁升级:偏向锁→轻量级锁→重量级锁; 修饰代码块靠
monitorenter/monitorexit字节码,方法靠ACC_SYNCHRONIZED标记。 - Lock(以 ReentrantLock 为例) Java 语言层面接口,底层基于 AQS 抽象队列同步器,用 CAS + CLH 双向等待队列实现,全程用户态自旋,竞争激烈才阻塞。
二、使用方式:隐式锁 vs 显式锁
- synchronized:自动加解锁,无需手动操作
- 同步方法 / 同步代码块,代码执行完毕、抛出异常都会自动释放锁,不会死锁;
synchronized (obj) { // 自动释放 } - Lock:手动加锁、手动解锁,必须在 finally 中释放,否则异常会永久持有锁,死锁
lock.lock(); try { // 业务逻辑 } finally { lock.unlock(); // 必须手动释放 }
三、灵活性与丰富功能(Lock 核心优势)
1. 可中断等待
- synchronized:线程阻塞等待锁时,无法被中断,只能死等;
- Lock:
lockInterruptibly(),等待过程中可响应interrupt(),抛出中断异常,方便取消任务。
2. 支持超时等待
- synchronized:没有超时机制,拿不到锁无限阻塞;
- Lock:
tryLock(long time, unit)指定等待时间,超时拿不到直接返回 false,避免死锁。
3. 非阻塞尝试获取锁
tryLock() 立刻尝试,拿到返回 true,拿不到直接返回,不阻塞线程。
4. 公平锁 / 非公平锁自由切换
- synchronized:永远是非公平锁;
- ReentrantLock 构造参数可指定:new ReentrantLock (true) 公平锁,false 非公平(默认)。
5. 多条件变量 Condition
- synchronized 只能配合对象 wait/notify/notifyAll,一个锁只能对应一个等待队列;
- Lock 可创建多个 Condition,精准分组等待、精准唤醒,生产者消费者场景非常好用,区分 “队满”“队空” 两组等待线程。
四、锁类型:可重入性
两者都是可重入锁:同一线程多次获取同一把锁不会阻塞。
- synchronized:Monitor 内部计数器记录重入次数;
- ReentrantLock:AQS 的 state 变量记录重入次数。
五、性能表现
- JDK1.6 之前:synchronized 重量级锁,内核切换开销大,性能远差于 Lock;
- JDK1.6 及以后:引入偏向锁、轻量级锁自旋优化,无竞争场景两者性能几乎持平;
- 高并发激烈竞争场景:Lock 自旋优化、灵活中断 / 超时设计,吞吐量通常优于 synchronized。
六、锁释放规则
- synchronized:自动释放,正常结束 / 抛异常都会释放,不会漏;
- Lock:必须手动 unlock,代码异常忘记写 finally 释放,锁永久占用,其他线程永久阻塞。
七、适用场景总结
- 简单同步逻辑、代码量少、低并发场景:优先 synchronized,写法简洁不易出错;
- 需要中断等待、超时放弃、公平锁、多条件精准唤醒、高并发场景:使用 Lock。
精简一句话总结
synchronized 是 JVM 隐式锁,自动释放、使用简单,功能单一;Lock 是 API 显式锁,底层 AQS 实现,支持中断、超时、公平锁、多 Condition,灵活性更强,但需要手动在 finally 解锁。
更多推荐




所有评论(0)