在Java中,为了提高多线程程序的性能,引入了不同级别的锁,包括偏向锁(Biased Locking)、轻量级锁(Lightweight Locking)和重量级锁(Heavyweight Locking)。这些锁在不同的场景下有各自的适用性和效率,JVM会根据情况自动升级或降级锁的级别。
偏向锁
偏向锁是最轻量级的锁,它的核心思想是,如果一个线程获得了锁,那么锁就进入偏向模式,当这个线程再次请求锁时,就无需进行同步。偏向锁适用于只有一个线程访问同步块的场景。
- 启用时机:启动JVM时默认启用,可以通过
-XX:+UseBiasedLocking
和-XX:-UseBiasedLocking
来开启或关闭。 - 特点:极大地提高了带有同步但实际上只有单线程访问的代码块的性能。
轻量级锁
当偏向锁不能使用(例如,当有另一个线程尝试获取锁时)时,锁会升级为轻量级锁。轻量级锁通过在对象头上存储锁指针,并使用CAS(Compare and Swap)机制尝试获取锁,来避免线程阻塞。
- 特点:适用于线程交互时间短的情况,可以减少传统锁机制的开销,提高程序运行效率。
- 操作方式:通过堆栈中的锁记录(Lock Record)来将锁的状态存储在对象头中。
重量级锁
当轻量级锁的自旋失败或者锁竞争激烈时(即有多个线程同时试图获取同一个锁),锁会升级为重量级锁。重量级锁会使其他试图获取锁的线程阻塞,直到锁被释放。
- 特点:通过操作系统中的互斥量(Mutex)实现,适用于长时间的同步操作。
- 成本:相比偏向锁和轻量级锁,重量级锁的开销最大,因为涉及到操作系统的线程调度和状态切换。
锁的升级过程
- 偏向锁状态:当一个线程访问同步块时,会使用偏向锁。
- 升级为轻量级锁:如果另一个线程尝试获取同一个锁,则原有的偏向锁升级为轻量级锁。
- 升级为重量级锁:如果在尝试获取轻量级锁时失败(通过自旋),则锁会升级为重量级锁。
JVM通过这样的锁升级机制来平衡同步操作的开销与线程阻塞的成本,使得在不同竞争程度下都能尽可能保持效率。理解这三种锁的区别和应用场景,对于写出高性能的Java并发程序非常有帮助。