锁升级机制——Java全栈知识(49)
前言
在 JDK 1.6 之前,使用 synchronized 关键字需要依赖于底层操作系统的 Mutex Lock 实现,挂起线程和恢复线程都需要转入内核态来完成,也就是说阻塞或唤醒一个 Java 线程都需要系统去切换 CPU 状态,这种状态的切换需要消耗处理器时间。这也就是为什么 synchronized 属于重量级锁的原因,因为需要切换 CPU 状态导致效率低下,时间成本相对较高,特别是当同步代码的内容过于简单时,可能切换的时间还要比代码执行的时间长。
在 JDK 1.6 之后,引入了偏向锁与轻量锁来减小获取和释放锁所带来的性能消耗,也就是不再是一上来就需要切换 CPU 状态导致效率低下而是通过锁升级的方式逐步增大性能消耗,从而避免了一些无需使用重量级锁的情况的性能消耗问题。
三种锁的特点
锁类型 | 优点 | 缺点 |
偏向锁 | 只有一个线程访问同步代码时,只在置换ThreadID时进行一次CAS操作,锁的开销低,性能接近于无锁状态。 | 线程间存在竞争时,需要频繁暂停持有锁的线程并检查状态和撤销锁,反而带来额外的开销。 |
轻量级锁 | 线程间存在交替竞争时,竞争的线程不需要阻塞,提高了响应速度。 | 当大量线程存在竞争时,线程始终的抢占不到锁,会导致CPU空转消耗CPU性能。 |
重量级锁 | 通过阻塞唤醒的方式实现同步,防止CPU空转,不会消耗CPU性能。 | 线程阻塞导致响应时间变长,频繁切换CPU状态,导致性能消耗增大。 |
根据三类锁的优缺点可以知道使用场景
- 偏向锁:适用于单线程的情况,在不存在锁竞争的时候进入同步代码可以使用偏向锁。
- 轻量级锁:适用于竞争较不激烈且同步代码执行时间较短的情况,存在竞争时升级为轻量级锁,轻量级锁采用的是自旋锁,采用轻量级锁虽然会占用cpu资源但是相对比使用重量级锁要更高效。
- 重量级锁:适用于竞争激烈且同步代码执行时间较长的情况,此时使用轻量级锁自旋带来的性能消耗就比使用重量级锁更严重,这时候就需要升级为重量级锁。