引言
在Java中,synchronized是一个非常重要的关键字,它为开发者提供了一种便捷的方式来控制并发。但要充分利用它的威力,我们需要理解其内部的工作原理。本篇博客将深入探讨synchronized关键字背后的机制,特别是锁的升级过程。
偏向锁
在JDK1.6之前,Synchronized底层是调用了操作系统的Mutex Lock,所以使用Synchronized会从用户态向内核态转换,会有性能的损耗,在JDK 1.6引入了偏向锁,这是一种针对单线程的优化。它假设没有线程会竞争锁,因此直接将锁偏向于首次请求它的线程。如果在后续的执行过程中,该锁没有被其他线程请求,则持有偏向锁的线程无需再进行同步操作,这就避免了无竞争情况下的同步原语的开销。
轻量级锁
轻量级锁是针对多线程轮流进入同步块的场景进行优化的。当一个线程进入同步块时,如果同步对象未被锁定,JVM会在当前线程的栈帧中创建用于存储锁记录的空间,并将对象头的信息复制到锁记录中,再尝试用CAS将对象头的信息替换为指向锁记录的指针。如果成功,当前线程则获取到了锁。如果失败,表示其他线程竞争该锁,那么当前线程便尝试使用自旋来获取锁。
重量级锁
重量级锁是当有多个线程同时竞争同一把锁时的情况。当线程无法获得轻量级锁,且自旋等待的策略失败后,锁会膨胀为重量级锁。此时,无法获取锁的线程会进入操作系统的等待队列,陷入阻塞状态。这种状态下的线程调度需要切换到内核态进行操作,开销比前两种锁更大。
锁的升级过程
Java的锁升级过程是由偏向锁向轻量级锁,再向重量级锁进行升级。
从偏向锁升级到轻量级锁的触发条件是:当一个线程持有偏向锁,而另一个线程试图锁定同一对象时,偏向锁就会被撤销,锁会升级为轻量级锁 (只要有两个线程同时竞争,就会升级)。
从轻量级锁升级到重量级锁的触发条件是:在持有轻量级锁的线程还未释放锁的情况下,有其他线程试图获取该轻量级锁,并且自旋等待已经失败 (竞争激烈,自选次数超过阈值)。
这种锁的升级设计考虑了减少锁的竞争对系统性能的影响。在无锁竞争和轻度竞争的情况下,通过偏向锁和轻量级锁减少不必要的重量级锁导致的线程上下文切换。在激烈的锁竞争情况下,使用重量级锁,让线程进入阻塞状态,从而让CPU有更多的时间处理其他任务。
结语
理解synchronized的内部原理和锁的升级过程,对于编写高效的并发代码是非常有帮助的。希望本篇博客能帮助你理解和掌握这个重要概念。在并发编程的道路上,我期待你能走得更远,成就更大。