【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使用率飙升,

所以此时需要引入重量级锁,来达到服务器性能上的平衡。

了解其他:

Sychronized底层加锁原理详解 》

从synchronized锁优化来了解自适应自旋锁、锁消除和锁粗化 》

原文地址:https://www.cnblogs.com/boluopabo/p/13055090.html