【Java】唠唠synchronized中的轻量级锁
- 说到轻量级锁,我们必须先说一下轻量级锁是什么?
synchronized在JDK1.6之后的优化锁后,一共有四种锁阶段:
无锁 --> 偏向锁 --> 轻量级锁 --> 重量级锁
而轻量级锁,正处于是第三种阶段。
那么如何才会触发偏向锁升级为轻量级锁?偏向锁又是如何升级为轻量级锁的呢?
1》如何触发偏向锁升级为轻量级锁呢?
①线程A此时已占有锁对象资源,锁对象【Mark Word】中线程ID指向线程A;
②线程B此时访问同步代码块,试图抢占资源,通过CAS修改Mard Word,但由于线程A已占有锁对象资源,
此时CAS修改失败;
③那么此时将会触发偏向锁升级为轻量级锁
2》偏向锁在升级为轻量级锁时,会先撤销偏向锁,那么是如何撤销的呢?
①首先等线程A在安全节点阻塞(类似于GC前线程在安全节点阻塞);
②判断线程A是否还存活,如果线程A还存活;
③判断线程A是否继续竞争锁,如果不竞争,线程B获得偏向锁;
④如果竞争,则线程A和线程B同时升级为抢占轻量级锁
- synchronized和对象表头(即Mark Word)的关系可以说是息息相关,那么我们先看一下各个锁级别表头情况:
- 由与轻量级锁的Mark Word只剩下了【指向线程栈中锁记录的指针】和【锁标志位】,那么具体是什么格式呢?
大致流程是这样:
1》偏向锁撤销之后(偏向状态为0),现在无论是A线程还是B线程执行同步代码块进行加锁,都在自己的线程帧栈中创建一个锁记录【Lock Record】; 2》线程A此时抢占锁,将Object中的【Mark Word】拷贝到线程栈中的【Lock Record】中的【displaced hdr】; 3》将锁记录中的【owner】指针指向加锁的对象; 4》将锁对象的对象头【Mard Word】替换为锁记录的指针; 5》此时锁标志位是00,为轻量级锁。
- 那么我看下如下代码,synchronized又是如何实现可重入的呢?
synchronized(obj){
synchronized(obj){ //①
synchronized(obj){
}
}
}
synchronized在轻量级锁上的重入原理可以这样理解:
线程栈中有一个初始的【Lock Record】,其中【displaced hdr】是从【Mard Word】中拷贝出来的,
当代码执行到①处时,此时会向线程栈中添加一个【displaced hdr】为NULL的【Lock Record】,
如果继续使用synchronized进行重入时,会继续向线程栈中添加【displaced hdr】为NULL的【Lock Record】对象。
释放锁的原理我脚的大家应该猜到了,就是像栈一样,从下至上每次排出一个【Lock Record】。
- 轻量级锁解锁过程是个什么样的呢?
当线程在轻量级锁解锁过程,先通过CAS将线程栈中锁记录【Lock Record】的【Diplaced Header】,
替换至锁对象中。
若替换成功,说明线程已经执行完同步代码块,该期间没有任何多余线程进行竞争,轻量级锁解锁。
若替换失败,说明线程还未执行完同步代码块,若此时锁对象资源仍存在竞争,并有其他线程加入竞争,则锁将膨胀至重量级锁。
- 轻量级锁的优缺点
轻量级锁优点主要是采用自旋的方式来处理线程竞争的,自旋操作可以避免线程阻塞、唤醒等操作,从而减少线程上下文切换而对服务器性能的消耗。
不过如果存在某线程占用锁资源时间过长,从而导致其他线程长时间处于自旋状态,会严重消耗CPU资源,使得CPU使用率飙升,
所以此时需要引入重量级锁,来达到服务器性能上的平衡。
了解其他: