java并发编程-sychronized(4)

126 阅读3分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第10天,点击查看活动详情

无锁

不对资源进行锁定,所有线程都能够同时对该资源进行操作。但是只有一个线程能够操作成功,其他失败的线程会不断循环进行尝试重新操作指导成功CAS

缺点:不断循环会消耗CPU资源

偏向锁

获得偏向锁

当一个线程访问并获得锁,会在对象头中23bit记录下该线程ID,并设置1bit的偏向锁标志为1.(CAS)

获得锁的线程再下次访问时不需要获取锁,可以直接获得锁执行任务

释放偏向锁(不是释放)-由于竞争导致不能再使用偏向锁,将markword第三位变成0

当有竞争出现时,就会撤销偏向锁。

当有其他线程线B程访问,发现1bit的偏向锁标志为1,但是存储的线程ID-A不是自己。那么先暂停那个偏向锁的线程,判断它是否在活动状态。

  • 如果不在,就设置成无锁(线程设为空23bit去掉,撤销偏向锁1bit->0),偏向该线程

  • 如果存在,判断该线程现在要不要用。

    • 不持有就设置成无锁,偏向其他线程

    • 还在持有,那么当偏向线程执行到达安全点暂停偏向线程,升级成轻量级锁开始竞争

      • 其实有三种情况升级成轻量级锁是第一种情况,批量撤销次数1-20次
      • 批量重偏向:撤销次数20-39时,会直接将偏向锁指定成线程B
      • 批量撤销:批量次数大于40个,禁用偏向锁,锁标志为直接变成00(轻量级)

偏向锁不会主动释放,也就是说它不会在工作完成后给你恢复ThreadID等等信息,只有在其他线程来竞争时,才会被释放。因为偏向锁主要做的事就是为了可重入

批量重偏向和批量撤销

当有线程竞争获得锁时,需要等到安全点再将锁撤销或者升级,需要消耗资源。

为class维护一个偏向锁撤销计数器,每次实例对象撤销,计数器+1。

计数器小于20,直接升级成轻量级锁

当计数器到达20,JVM就会认为该class偏向锁有问题,会触发批量重偏向。不升级,偏向新线程。

epoch: 本质是个时间戳,class和处于偏向锁的对象有该字段

  • 当遇到安全点时,对class 的epoch进行+1,然后遍历JVM所有线程栈,找到该class的所有处于偏向锁的实例对象,将其的epoch赋上新值。
  • 获得锁时,对象的epoch和class的epoch不一致,将CAS线程id为新线程

当计数器到达40,JVM认为该class的场景存在多线程竞争(多线程不适合偏向锁,所以直接跳过),就会标记class不可用,之后会该class的锁,直接走轻量级锁逻辑

流程图

image-20220304102639920.png