【707、synchronized 锁升级的原理】

68 阅读2分钟

在Java中,synchronized关键字可以用于实现线程之间的同步。当一个线程进入synchronized代码块时,它会获取到一个锁,其他线程想要执行同步代码块就需要等待该锁的释放。在Java的早期版本中,synchronized锁的实现比较简单,被称为偏向锁(biased lock)。

偏向锁适用于大多数情况下只有一个线程访问同步代码块的场景。当一个线程进入同步代码块时,它会尝试获取偏向锁,如果成功获取,那么这个线程就可以进入临界区执行代码,而无需再进行额外的同步操作。如果其他线程想要进入同步代码块,它们需要等待偏向锁的释放。

然而,当多个线程竞争同步代码块的访问时,偏向锁就无法满足需求,因为它只能支持单线程的快速访问。这时,偏向锁会升级为轻量级锁(lightweight lock)。

轻量级锁使用了一种乐观的锁策略。当第一个线程进入同步代码块时,它会尝试获取轻量级锁,并将对象头中的一部分数据复制到线程栈帧中的锁记录(Lock Record)中。其他线程进入同步代码块时,会发现有其他线程已经获取了轻量级锁,于是它们会自旋(Spin)一段时间,不断尝试获取锁。自旋是一种忙等待的策略,目的是为了避免线程进入阻塞状态,提高同步性能。

如果自旋尝试获取锁的线程仍然无法获得锁,那么轻量级锁就会升级为重量级锁(heavyweight lock)或者是对象监视器锁(Object Monitor)。重量级锁使用操作系统的互斥量来实现线程的同步,它会使得线程进入阻塞状态,并进行操作系统级别的上下文切换。

需要注意的是,锁的升级过程是逐级升级的,也就是说,从偏向锁到轻量级锁,再到重量级锁。这种升级策略是为了尽量减少对性能的影响,在同步竞争较小的情况下,能够快速地获取锁而不引入过多的开销。