在Java中,synchronized关键字用于实现对象级别的锁定,以确保多个线程对共享资源的安全访问。在进行synchronized锁的升级过程中,涉及到多次自旋,接下来我将从原理层详细介绍。
1. 锁的升级过程
当一个线程尝试获取某个对象的锁时,会经历以下几个阶段:
(1)偏向锁(Biased Locking)
初始时,对象的头部信息标记为偏向锁。当第一个线程访问这个对象时,对象头部的偏向锁标记会指向这个线程,并且之后该线程再次访问同步块时,无需再次竞争锁。这个阶段不涉及自旋,是一种快速获取锁的机制。
(2)轻量级锁(Lightweight Locking)
当有多个线程竞争同一个锁时,偏向锁会升级为轻量级锁。此时,系统会通过CAS(Compare And Swap)操作尝试在对象头部设置指向栈中锁记录的指针。如果竞争不激烈,其他线程会进入自旋(自旋次数有限制),尝试获取锁,避免进入阻塞状态。
(3)自旋锁(Spin Locking)
如果轻量级锁的竞争仍然激烈,没有获得锁的线程将进入自旋状态,不断重试获取锁,而不是立即进入阻塞状态。这样可以避免线程频繁地在用户态和内核态之间切换,提高性能。
(4)锁膨胀(Lock Coarsening)
如果自旋一定次数后仍未获取到锁,轻量级锁将升级为重量级锁,此时竞争激烈的锁将使用传统的互斥量来保护,等待队列中的线程将进入阻塞状态,直到持有锁的线程释放锁。
2. 自旋的原理
自旋是一种在多线程环境下等待锁的技术,它能减少线程阻塞带来的系统开销。自旋的实现原理基于处理器提供的指令集,通常是通过在一个循环中不断地检查锁的状态,直到成功获取锁或者达到最大自旋次数。
3. 自旋的次数
在Java中,自旋的次数是可以配置的,默认情况下是 10 次。如果在指定次数内未能获取到锁,线程将转而进入阻塞状态,以避免无谓的占用 CPU 资源。自旋次数的设定需要根据具体场景进行合理调整,以平衡性能和资源利用。
总之,synchronized 锁的升级过程中涉及多轮自旋,通过偏向锁、轻量级锁和自旋锁等机制,系统可以在一定程度上提高并发性能,同时避免线程频繁地进入阻塞状态带来的系统开销。