什么是锁:
每一个 JAVA 对象都会与一个监视器 monitor 关联,我们 可以把它理解成为一把锁,当一个线程想要执行一段被 synchronized 修饰的同步方法或者代码块时,该线程得先 获取到 synchronized 修饰的对象对应的 monitor。
monitorenter 表示去获得一个对象监视器。monitorexit 表 示释放 monitor 监视器的所有权,使得其他被阻塞的线程 可以尝试去获得这个监视器 monitor 依赖操作系统的 MutexLock(互斥锁)来实现的, 线程被阻塞后便进入内核(Linux)调度状态,这个会导致系统在用户态与内核态之间来回切换,严重影响锁的性能。
偏向锁
当 Thread#1 进入临界区时,JVM 会将 lockObject 的 对象头 Mark Word 的锁标志位设为“01”,同时会用 CAS 操作把 Thread#1 的线程 ID 记录到 Mark Word 中,此时进 入偏向模式。所谓“偏向”,指的是这个锁会偏向于 Thread#1, 若接下来没有其他线程进入临界区,则 Thread#1 再出入 临界区无需再执行任何同步操作。也就是说,若只有 Thread#1 会进入临界区,实际上只有 Thread#1 初次进入 临界区时需要执行 CAS 操作,以后再出入临界区都不会有 同步操作带来的开销。
轻量级锁
偏向锁的场景太过于理想化,更多的时候是 Thread#2 也 会尝试进入临界区, 如果 Thread#2 也进入临界区但是 Thread#1 还没有执行完同步代码块时,会暂停 Thread#1 并且升级到轻量级锁。Thread#2 通过自旋再次尝试以轻量 级锁的方式来获取锁
重量级锁
如果 Thread#1 和 Thread#2 正常交替执行,那么轻量级锁 基本能够满足锁的需求。但是如果 Thread#1 和 Thread#2 同时进入临界区,那么轻量级锁就会膨胀为重量级锁,意 味着 Thread#1 线程获得了重量级锁的情况下,Thread#2 就会被阻塞