Java并发-synchronized实现原理
Java并发-synchronized实现原理
xukunSynchronized的实现原理
synchronized实现原理依赖于JVM的Monitor
(监视器锁)和对象头
(Object Header)。
当synchronized修饰在方法或代码块上时,会对特定的对象或类加锁,从而确保同一时刻只有一个线程能执行加索的代码块。
synchronized修饰方法:方法的常量池会增加一个
ACC_SYNCHRONIZED
标志,当某个线程访问这个方法检查是否有ACC_SYNCHRONIZED
标志,若有则需要获得监视器锁才可以执行方法,保证方法的同步。synchronized修饰代码块:会在代码块的前后插入
monitorenter
和monitorexit
字节码指令。可以把monitorenter
理解为加锁,monitorexit
理解为解锁。
synchronized关键字可以修饰代码块、实例方法和静态方法,本质上都是作用于对象。
对象头(Object Header)
在JVM中,每个对象的内存布局主要有两部分组成:
Mark Word:用于存储对象的运行时数据,包括锁状态、哈希码、GC分代信息等。
KIass Pointer:指向对象的类型元数据,帮助JVM确定对象的类型信息。
Mark Word
是实现synchronized的关键,因为他会根据锁的状态保存不同的信息,具体包括:
- 未锁定状态
- 偏向锁状态
- 轻量级锁状态
- 重量级锁状态
锁的升级与优化
为了提高synchronized的性能,JVM从JDK1.6
开始引入了锁的优化机制,包括偏向锁
、轻量级锁
、重量级锁
,这些状态会根据线程的竞争情况就行动态升级
或降级
。
偏向锁
在没有锁竞争的情况下,锁总是“偏向”与第一个获得它的线程。偏向锁通过减少不必要的CAS操作来提高性能。
加锁过程:当线程第一次请求锁时,JVM会将该线程的ID记录在对象头的
Mark Word
中,表示锁偏向于该线程。后续该线程再进入该锁时,无需进行额外的同步操作。撤销偏向锁:如果在偏向锁持有期间,另一个线程请求同一把锁,JVM会撤销偏向锁,并升级为轻量级锁。
轻量级锁
轻量级锁适用于多个线程短时间内争用同一锁的场景。
加锁过程:当线程进入同步块时,JVM会在当前线程的栈帧中创建一个锁记录,并将对象头中的Mark Word拷贝到锁记录中。线程尝试使用
CAS操作
将对象头中的Mark Word
更新为指向锁记录的指针。如果成功,则表示该线程获取了锁;如果失败,则表示其他线程已经持有该锁,此时锁会升级为重量级锁。解锁过程:线程退出同步块时,JVM会将对象头中的
Mark Word
恢复为原始值。
重量级锁
当锁竞争激烈时,JVM会升级为重量级锁,重量级锁使用操作系统的互斥量(Mutex)
机制来实现线程的阻塞于唤醒。
- 加锁过程:如果线程无法通过轻量级锁获取锁,JVM会将该锁升级为重量级锁,并将当前线程阻塞。
- 当线程释放重量级锁时,JVM会唤醒所有阻塞的线程,允许它们再次尝试获取锁。
锁升级总结
- 偏向锁:当一个线程第一次获取锁时,JVM会将该线程标记为“偏向”状态,后续若该线程再次获取该锁,几乎没有开销。
- 轻量级锁:当另一个线程尝试获取已经被偏向的锁时,锁会升级为轻量级锁,使用
CAS
操作来减少锁竞争的开销。 - 重量级锁:当
CAS
失败无法获取锁,锁会升级为重量级锁,线程会被挂起,直到锁被释放。
Synchronized的可重入性
synchronized是可重入的,每获取一次锁,计数器+1
,释放锁时,计数器-1
,直到为0
,释放锁。