关于我们

质量为本、客户为根、勇于拼搏、务实创新

< 返回新闻公共列表

JVM锁优化

发布时间:2023-06-26 14:00:07

JVM之锁优化 锁总结

  • Java对于锁的优化:归根结底是权衡操作系统中用户态、核心态切换的资源损耗与CPU空转资源损耗
  • 偏向锁适用于单线程无竞争的情况
  • 轻量级锁适用于存在线程竞争,但是不激烈的情况
  • 重量级锁适用于竞争激烈的情况(当CAS的损耗>操作系统用户态、核心态切换的损耗时,转变为重量级锁)

自旋锁

  • 为什么引入自旋锁?
    • 在实现互斥同步时,我们需要借助操作系统;在这个过程中,可能会存在用户态到和核心态之间的切换(这个切换十分消耗资源)
    • 引入自旋锁:我们默认当前线程阻塞的时间很短,当前线程可以很快获取锁;而不要依赖操作系统,让当前线程处于一个循环的状态(自旋)
    • 目的:通过自旋来降低操作系统状态转换、线程切换的开销
    • 自旋锁存在问题:
    • 假设当前线程无法短时间获取处理器执行权,则线程会处于一个持久的空转状态(自旋);这个空转所造成的开销可能导致大于线程切换的开销

自适应锁

  • 自适应锁:
    • 解决自旋时间过长、过度消耗资源的问题
    • 通过上一次自旋的时长来决定下一次自旋的时长(如果上一次自旋过长,则认为我将难以获取锁;延长我自旋的时间,甚至放弃自旋;若上一次自旋时间很短,则认为我也很容易获取锁,降低自旋的时间)
  • 锁消除:
    • 指JVM在对代码进行编译时,对一些代码要求同步,但是经JVM检测不可能发生数据竞争,进而消除锁
    • 如代码块:
    • StringBuffer类的每一个方法都需要synchronized修饰,但是stringBuffer永远无法逃逸出sysTest方法作用域,故而不会发生任何的数据竞争
  • 锁粗化:
    • 指需要对一个对象进行重复加锁、释放锁;这是性能损耗也很高,锁粗化为第一个操作开始前以及最后一个操作执行后;
    • 如代码块:
  • 对象MarkWord布局图:
  • 偏向锁
    • 只比较当前线程的ID与被锁对象的对象头MarkWord中的线程ID;不进行CAS操作
    • 当锁对象第一次被线程获取时,ThreadID写入锁对象的MarkWord,修改标志位为01,偏向模式为1
    • 当ThreadID与对象头ThreadID不一致时(发生竞争时);修改标志位为01或00、偏向模式为0(撤销偏向锁,升级为轻量级锁)
  • 轻量级锁
    • 通过CAS操作,减少重量级锁的使用,进而减少重量级锁所带来的性能损耗
    • 1.当锁的标置位为01时,当前线程栈会新建锁记录(Lock Record)用以记录被锁对象的MarkWord
    • 2.而后会通过CAS将对象头的MarkWord更新为指向线程栈的Lock Record的指针;
    • 3.如果更新成功,也就代表了当前线程拥有了锁,同时修改标志位为00
    • 4.如果更新失败,则说明至少存在一个线程与当前线程竞争
    • 4.1 再次比较MarkWord指针是否指向当前线程栈帧;若指向当前栈帧,则执行同步代码块
    • 5.重复执行若干次轻量级锁CAS操作后,若对象头的MarkWord指针还是未指向当前线程栈,则锁升级为重量级锁(轻量级锁->重量级锁)
  • 重量级锁
    • 在轻量级锁进行若干次CAS操作后,被锁对象的MarkWord指针依然没有指向当前线程栈,则升级为重量级锁;
      • 标志位修改为10,此时被锁对象的MarkWord指向重量级锁(互斥量,依赖于操作系统);而后所有的等待线程需进入block状态
  • 锁升级流程图(CV大法):

/template/Home/leiyu/PC/Static