synchronized底层原理
synchronized是基于JVM内置锁实现,通过内部对象Monitor(监视器锁)实现,基于进入与退出Monitor对象实现方法与代码块同步,监视器锁的实现依赖底层操作系统的Mutex lock(互斥锁)实现,它是一个重量级锁性能较低。
JVM内置锁在1.5之后版本做了重大的优化,如锁粗化(Lock Coarsening)、锁消除(Lock Elimination)、偏向锁(Biased Locking)、轻量级锁(Lightweight Locking)、适应性自旋(Adaptive Spinning)等技术来减少锁操作的开销,内置锁的并发性能已经基本与Lock持平。
synchronized锁升级过程
synchronized锁升级过程也是synchronized锁优化的过程。synchronized锁升级的过程:无锁(锁对象初始化)->偏向锁(有线程请求锁)->轻量级锁(多线程轻度竞争)->重量级锁(多线程重度竞争)。锁的升级是单向的,不会出现锁的降级。
- 无锁:锁对象初始化时,所对象处于无锁状态;
- 偏向锁:有线程请求锁时,升级为偏向锁,主要处理的是同一线程多次获取同一把锁的情形。偏向锁的核心思想是:如果一个线程获得了锁,那么锁就进入偏向模式,当这个线程再次请求锁时,无需再做任何同步操作,即可获取锁,这样就省去了大量有关锁申请的操作,从而也就提高了程序的性能。偏向锁不会主动释放锁。(偏向锁将线程ID写入锁对象的markword)
JVM默认延迟4s开启偏向锁(此时为匿名偏向锁,不指向任何任务线程),可以对偏向锁进行相关设置。
取消延时:-XX:BiasedLockingStartupDelay=0
关闭偏向锁:-XX:-UseBiasedLocking = false; - 轻量级锁:偏向锁失败,多线程轻度竞争,此时synchronized升级为轻量级锁。竞争的线程自旋,进行CAS操作,消耗CPU资源。1.6之后,加入自适应自旋(Adaptive Self Spanning),由JVM自行控制自旋次数。在线程较少,线程占用时间较短的情况下,轻量级可以提高效率。
- 重量级锁:多线程重度竞争,切换到内核态(os)向操作系统申请锁,进行锁的分配。等待线程进入阻塞队列,不会导致CPU空转消耗CPU资源。
锁对象头
锁粗化
将多个连续的加锁,解锁操作连接在一起,扩展成一个范围更大的锁。
锁消除
锁消除是指虚拟机即时编译器在运行时,对一些代码上要求同步,但是被检测到不可能存在共享数据进行竞争的锁进行消除。
synchronized实现过程
- Java代码:synchronized修饰方法或代码块
- 字节码:monitorenter,monitorexit
- 执行:执行过程中自动进行锁的升级
- CPU汇编:lock cmpxchg指令