美文网首页
08给女朋友讲讲并发编程-轻量级锁、锁膨胀、自旋、锁消除、偏向锁

08给女朋友讲讲并发编程-轻量级锁、锁膨胀、自旋、锁消除、偏向锁

作者: XueFengDong | 来源:发表于2021-01-06 22:20 被阅读0次

一、轻量级锁

在多线程条件下,虽然一个对象会有多个线程访问,但是他们访问的时间是错开的(没有竞争关系),那么可以使用轻量级锁来优化。

1.使用轻量级锁的目的

降低无实际竞争关系的情况下,直接使用重量级锁带来的性能消耗。

2.轻量级锁的使用

轻量级锁对使用者是透明的,语法仍然是synchronized.
假设有两个方法同步块,对同一个对象加锁。

static final Object object = new Object();

public static void method1(){
     synchronized(obj){
          //同步块A
          method2();
      }
}

public static void method2(){
      synchronized(obj){
        //同步块B
      }
}
  • 创建锁记录对象(Lock Record),每个线程的栈帧都会包含锁记录结构,内部可以存储锁定对象的Mark Word 以及对象的指针。


    轻量级锁-01
  • 让锁记录中的object reference指向锁对象,并尝试用cas替换object中的Mark Word,将Mark Word的值与Lock Record中的地址互换。


    轻量级锁-02
  • 如果cas成功,那么对象头的Mark Word变成了lock record,锁标记从01(无锁状态)变成了00(轻量级锁),此时该线程加锁成功。

二、锁膨胀

问:上述执行轻量级锁的过程中,如果cas失败了,怎么办?

  • 如果cas失败,说明存在竞争关系,需要升级为重量级锁,也称为锁膨胀。
    锁膨胀的过程
  • 当Thread-0在执行同步代码块的过程中,Thread-1也来使用轻量级锁的方式去获取锁,发现对象头中的Mark Word锁标记已经从01变成了00,Thread-1加轻量级锁失败,进入锁膨胀流程。
    锁膨胀过程01
  • 为object申请Monitor锁,让Object指向Monitor地址,锁标记从00变成10
  • 然后自己进入Monitor的阻塞队列entryList中,变成BLOCKED状态。
  • 当Thread-0执行完同步代码块后,释放轻量级锁的时候发现Mark Word中的地址已经发生改变,解锁失败,将进入Monitor锁的解锁流程。


    锁膨胀过程02

三、自旋

如果持有锁的线程很快就能将锁释放,那么其余线程就不需要进入EntryList阻塞队列中等待(内核态与用户态之间的切换进入阻塞状态)。它们只需要短时间的等待(自旋),等待持有锁的线程释放锁后,即可不用进入阻塞队列直接获取锁。

  • 自旋也会消耗cpu的资源,如果自旋执行时间太长,会有大量的线程处于自旋状态而占用cpu资源,进而会影响整体的性能。那么如何去选择自旋的执行时间呢?
    JVM默认的限定次数是10次,超过线程也会进入EntryList阻塞队列中等待。

四、锁消除

static int x = 0;

public void methodA(){
    x++;
};

public void methodB(){
    Object obj = new Object();
    synchronized(obj){
        x++;
    }
}

上述代码中methodA()和methodB()方法不同点是methodB()方法对局部变量对象加锁去执行++操作,理论上synchronized会影响性能,降低代码的执行效率。但是通过Benchmark对两个方法进行测试,发现两个方法的执行时间几乎没有差别。
为什么:
JVM中重要的核心模块之一 JIT即时编译器,它可以对字节码文件进一步优化,它会发现上述代码中的局部变量根本不会逃离方法的作用范围,就意味着这个对象不可能被共享,那么methodB()中的synchronized关键字也就显得没有意义了,JIT即使编译器就会将无意义的代码优化掉,也就是锁消除

  • 关闭锁消除优化
-XX:-EliminateLocks

五、偏向锁

顾名思义,他会偏向第一个访问锁的线程,如果在运行过程中,同步锁只有一个线程访问,不存在别的线程来使用的情况,就会给线程加一个偏向锁。锁标记为1,没有偏向锁则为0。
如果在执行过程中,遇到了其他线程抢占锁,则会升级为轻量级锁。
JVM默认启用偏向锁,在竞争激烈的场合,偏向锁会增加系统负担。
关闭偏向锁

-XX:-UseBiasedLocking

JVM启用偏向锁时,默认会有4s的延迟。原因在于,系统刚启动时,一般数据的竞争是比较激烈的,此时启用偏向锁会降低性能。
修改偏向锁延迟时间

-XX:BiasedLockingStartupDelay=0

相关文章

网友评论

      本文标题:08给女朋友讲讲并发编程-轻量级锁、锁膨胀、自旋、锁消除、偏向锁

      本文链接:https://www.haomeiwen.com/subject/hjrboktx.html