Java-锁升级

偏向锁/轻量级锁/重量级锁

Java-锁升级

java 中的锁 -- 偏向锁、轻量级锁、自旋锁、重量级锁_朱清震的博客-CSDN博客_java什么是自旋
之前做过一个测试,详情见这篇文章《多线程 +1操作的几种实现方式,及效率对比》,当时对这个测试结果很疑惑,反复执行过多次,发现结果是一样的: 1. 单线程下synchronized效率最高(当时感觉它的效率应该是最差才对); 2. AtomicInteger效率最不稳定,不同并发情况下表现不一样:短时间低并发下,效率比synchronized高,有时甚至比LongAdder还高出一点,但是...
偏向锁、轻量级锁、重量级锁、自旋锁、自适应自旋锁 - 云+社区 - 腾讯云
偏向锁就是在运行过程中,对象的锁偏向某个线程。即在开启偏向锁机制的情况下,某个线程获得锁,当该线程下次再想要获得锁时,不需要重新申请获得锁(即忽略synchro...

锁升级

JDK较早版本 OS的资源 互斥量 用户态->内核态的转换 重量级 效率比较低
现代版进行优化
无锁->偏向锁->轻量锁(自旋锁)->重量级锁

只有偏向锁1bit那个标记,偏向才有意义:1 可偏向、2 不可偏向

偏向锁

把当前线程指针贴到markword上

什么是偏向锁?

只偏向一个线程。markword上记录当前线程指针,下次同一个线程加锁的时候,不需要争用,只需要判断线程指针是否同一个,所以,偏向锁,偏向加锁的第一个线程,hashCode备份在线程栈上,线程销毁,锁降级为无锁

偏向锁默认是启动的,当JVM启动后,会延迟4s,偏向锁才启动。
为什么?
因为JVM启动时,内部好多方法需要上锁同步的过程,例如加载class过程,需要对某些内存进行同步。

偏向锁不可重偏向、批量偏向、批量撤销

为何会有偏向锁?
  • 多数synchronized方法,在很多情况下,只有一个线程在运行
  • 例如
    • StringBuffer中的一些sync方法
    • Vector中的一些sync方法
偏向锁的获取流程

获取流程

  1. 查看Mark Word中偏向锁的标识以及锁标志位,若是否偏向锁为1且锁标志位为01,则该锁为可偏向状态。
  2. 若为可偏向状态,则测试Mark Word中的线程ID是否与当前线程相同,若相同,则直接执行同步代码,否则进入下一步。
  3. 当前线程通过CAS操作竞争锁,若竞争成功,则将Mark Word中线程ID设置为当前线程ID,然后执行同步代码,若竞争失败,进入下一步。
  4. 当前线程通过CAS竞争锁失败的情况下,说明有竞争。当到达全局安全点时之前获得偏向锁的线程被挂起,偏向锁升级为轻量级锁,然后被阻塞在安全点的线程继续往下执行同步代码。

偏向锁的释放流程

偏向锁只有遇到其他线程尝试竞争偏向锁时,持有偏向锁状态的线程才会释放锁,线程不会主动去释放偏向锁。偏向锁的撤销需要等待全局安全点(即没有字节码正在执行),它会暂停拥有偏向锁的线程,撤销后偏向锁恢复到未锁定状态或轻量级锁状态。

可参考知了好学

偏向锁什么时候升级成轻量级锁?

只要有外来线程竞争,撤销偏向锁,升级轻量锁,线程在自己线程栈生成LockRecord,用CAS操作尝试将对象的markword设置为指向自己这个线程的Lock Record指针,设置成功者得到锁。

偏向锁什么时候升级成重量级锁?

当存在重度竞争(耗时过长的操作,wait等),会升级成重量级锁。

偏向锁的效率一定比轻量锁的高吗?

不一定。明确知道某个资源会有多个线程竞争,偏向锁肯定会涉及锁撤销,这时候直接使用自旋锁。JVM启动过程会有很多线程竞争(明确),所以默认情况启动时不打开偏向锁。

轻量级锁

什么是轻量级锁?

轻量级锁需要占用CPU资源。

轻量级锁是相对于重量级锁而言的,使用时不需要申请互斥量。而是在没有多线程竞争的情况下,使用轻量级锁能够减少性能消耗,但是当多个线程同时竞争锁时,轻量级锁会膨胀为重量级锁。

轻量级锁的目标是,减少无实际竞争情况下,使用重量级锁产生的性能消耗,包括系统调用引起的内核态与用户态切换、线程阻塞造成的线程切换等。

轻量级锁什么时候升级成重量级锁?

JDK1.6以前

  • 自旋超过10次,升级为重量级锁,如果太多线程自旋CPU消耗过大,不如升级为重量级锁,进入等待队列(不消耗CPU),
  • 超过CPU核数二分之一,升级重量级锁

JDK1.6以后

  • 加入自适应自旋Adapative Self Spinning,JVM自己控制
  • 升级重量锁:->向操作系统申请资源,linux mutex,CPU从3级-0级系统调用,线程挂起,进入等待队列。

重量级锁

什么是重量级锁?
重量级锁和轻量级锁的区别?

重量级锁不占用CPU资源,将线程放到等待队列waitSet中,等待操作系统调度,再唤醒线程。这个过程算阻塞

为什么有自旋锁还需要重量级锁?
  • 自旋是消耗CPU资源的,如果锁的时间长,或者自旋线程多,CPU会被大量消耗。
  • 重量级锁有等待队列,所有拿不到锁的进入等待队列,不需要消耗CPU资源。

底层源码

synchronized编译成字节码后,可以看到一个monitorenter和两个monitor,这是因为包含了异常退出

查看JVM参数

java 

查看XX开头的参数
java -XX:+PrintFlagsFinal -version