synchronized锁升级原理
锁升级过程主要依赖 Mark Word 中偏向锁标志位和锁标志位
- 偏向锁:Mark Word存的是偏向的线程id
- 轻量级锁:Mark Word存的是指向线程栈中Lock Record的指针
- 重量级锁:Mark Word存的是指向堆中的monitor对象的指针
锁升级过程
锁升级过程其实就锁从偏向锁到轻量级锁再到重量级锁的过程。
举个例子:
- 线程1已经获取到锁,此时线程2来抢该对象的锁,由于线程1已经获取到锁,此时这锁已经是偏向锁。
- 线程2开始抢夺该对象的锁,但是发现对象头Mark Word中存储的线程ID是属于线程1的,线程2就会进行cas操作希望获得锁;
- 此时线程2操作有两种结果:1、获得锁成功:Mark Word中的线程ID替换成线程2的ID,此时锁偏向于线程2,该锁还是偏向锁。2、获取锁失败:则偏向锁就会升级成轻量级锁。此时轻量级锁仍属于线程1持有,继续执行其同步代码。而竞争线程2通过自旋等待获取锁。
- 自旋次数到了线程1还没有释放锁,或者线程1还在执行,线程2还在自旋等待,这时又有一个线程3过来竞争这个锁对象,那么这个时候轻量级锁就会膨胀为重量级锁。重量级锁把除了拥有锁的线程都阻塞,防止CPU空转(自旋的时间太长也不行,因为自旋是要消耗CPU的,因此自旋的次数是有限制的,比如10次或者100次)。
偏向锁
单线程竞争,当线程A第一次竞争到锁时,通过操作修改Mark Word中的偏向线程ID、偏向模式。
偏向锁只有遇到其他线程尝试竞争偏向锁时,持有偏向锁的线程才会释放锁,线程是不会主动释放偏向锁的。
轻量级锁
多线程竞争,但是任意时刻最多只有一个线程竞争,即不存在竞争太过劲烈的情况,也就没有线程阻塞。
升级时机
当关闭偏向锁功能或多线程竞争偏向锁会导致偏向锁升级为轻量级锁
加锁
JVM会为每个线程在当前栈桢中创建锁记录的存储空间(即:Displaced Mark Word),若一线程获取锁是发现是轻量级锁。会把锁的 Mark Word复制到自己Displaced Mark Word中。然后线程尝试用cas将锁的Mark Word替换成指向锁记录的指针。如果成功则获取锁,如果失败,表示Mark Word已经被替换成别的线程的锁记录。说明锁竞争。当前线程通过自旋获取锁。
锁释放
释放锁时,当前线程会使用cas操作会将Displaced Mark Word内容复制到Mark Word中。如果没有竞争,锁会释放成功。如果锁由于自旋次数到了或者有其他线程竞争而升级成重量级锁,cas操作会失败,释放锁时同时唤醒阻塞线程。
与偏向锁区别
轻量级锁每次退出同步块都需要释放锁,而偏向锁是在竞争发生时才释放锁。
重量级锁
基于Monitor对象实现的。monitor enter指令获取锁,monitor exit指令释放锁。
总结
synchronized锁升级的过程:先自旋,不行再阻塞。