synchronized
是 Java 中的一个关键字,用于实现同步。当多个线程同时访问一个共享资源时,为了避免出现竞态条件(race condition),需要对这些线程进行同步控制。在 Java 中,我们可以使用 synchronized
关键字来实现同步控制。
当一个线程尝试获取一个已经被其他线程获取的 synchronized
锁时,该线程会进入锁膨胀(lock escalation)的过程。在锁膨胀的过程中,JVM 会尝试将轻量级锁(lightweight lock)升级为重量级锁(heavyweight lock),以提高同步控制的效率和可靠性。
具体来说,锁膨胀的过程如下:
- 初始状态:当一个线程尝试获取一个对象的锁时,如果该对象的锁状态为无锁状态(unlocked),则该线程会尝试使用 CAS 操作将该对象的锁状态设置为轻量级锁状态(lightweight locked)并将当前线程 ID 记录在对象头中。
- 轻量级锁:如果 CAS 操作成功,那么该线程就拥有了该对象的轻量级锁,并可以直接访问该对象。
- 自旋等待:如果 CAS 操作失败,那么表示该对象已经被其他线程获取了轻量级锁。此时,当前线程会进入自旋(spin)状态,使用自旋锁等待其他线程释放该对象的轻量级锁。
- 自旋锁优化:在自旋等待的过程中,JVM 还会进行一些优化,例如自适应自旋(adaptive spin),即根据上一次自旋等待的时间来决定下一次自旋等待的时间。这些优化可以提高同步控制的效率。
- 锁膨胀:如果自旋等待超过了一定的次数或时间,那么 JVM 就会将该对象的锁状态升级为重量级锁状态(heavyweight locked)。此时,当前线程会进入阻塞状态,并将自己加入到该对象的等待队列中,等待其他线程释放该对象的锁。重量级锁是基于操作系统的互斥量(mutex)实现的,因此会涉及到用户态和内核态之间的切换,开销较大。
- 锁降级:如果在阻塞等待的过程中发现该对象的锁状态可以降级为轻量级锁或无锁状态,那么 JVM 就会将该对象的锁状态降级,并唤醒等待队列中的线程。这样可以减少阻塞等待和用户态和内核态之间的切换开销。