轻量级锁

适用场景

一般用于两个线程在交替使用锁的时候,由于没有同时抢锁,属于一种比较和谐的状态,就可以使用轻量级锁

 

加锁

线程在执行同步代码块之前,JVM会先在当前线程的栈帧中创建用于存储锁记录的空间,并将对象头中的Mark Word复制到锁记录中,官方称为Displaced Mark Word。然后当前线程尝试使用CAS将对象头中的Mark Word替换为指向所记录的指针。如果成功,当前线程获得锁,如果失败,表示其他线程竞争锁,当前线程便尝试使用自旋来获取锁。

 

解锁

轻量级解锁时,会使用原子CAS操作将Displaced Mark Word替换回到对象头,如果成功,则表示没有竞争发生。如果失败,表示当前锁存在竞争,锁就会膨胀成重量级锁。

 

锁膨胀流程图

自旋锁

轻量级锁在加锁过程中,用到了自旋锁。所谓自旋,就是指当有另外一个线程来竞争锁时,这个线程会在原地循环等待,而不是把该线程给阻塞,知道哪个获得锁的线程释放锁以后,这个线程可以马上获得锁。

因为绝大多数情况下线程获得锁和释放锁的过程都是非常短暂的,自旋一定次数之后极有可能碰到获得锁的线程释放锁,所以,轻量级锁适用于那些同步代码块执行很快的场景,这样,线程原地等待很短的时间就能够获得锁了。

注意:锁在原地循环等待的时候,是会消耗CPU资源的。所以自旋必须要有一定的条件控制,否则如果一个线程执行同步代码块的时间很长,那么等待锁的线程会不断的循环反而会消耗CPU资源。默认情况下锁自旋的次数是 10 次,可以使用-XX:PreBlockSpin参数来设置自旋锁等待的次数。

 

自适应自旋锁

在JDK7开始,引入了自适应的自旋锁,修改自旋次数的JVM的参数被取消,虚拟机不再支持由用户配置自旋锁次数,而是由虚拟机自动调整。自适应意味着自旋的次数不是固定不变的,而是根据前一次在同一个锁上自旋的时间及锁的拥有者的状态来决定。如果在同一个锁对象上,自旋等待刚刚成功获得过锁,并且持有锁的线程正在运行中,那么虚拟机就会认为这次自旋也是很有可能再次成功,进而它将允许自旋等待持续相对更长的时间。如果对于某个锁,很少成功获得过,那在以后尝试获取这个锁时将可能省略掉自旋过程,直接线程阻塞,避免浪费CPU资源。

原文地址:https://www.cnblogs.com/lch-Hao/p/14490685.html