前言
- 上文学习了Java对象内存分布模型。
- 同时也了解到了锁的信息时存储在
markword
头信息中的。之前我们也通过几个案例分析了markword的存储情况。这里需要注意的是不要使用lombok插件才能看到hashcode。 - java中针对并发场景的问题提出了锁的概念,上锁我们可以使用原生的
synchronized
也可以使用Lock
相关锁。同时结合volatile使用实现线程之间通信。关于锁的使用我们下章节详谈,今天我们来了解锁内部有关锁的状态或者说不同时间的不同状态锁及其切换过程 - 在JDK1.6之前synchronized就是重量级,1.6之后JDK对内部锁进行重新定义:偏向锁、轻量级锁、重量级锁
三者性能分析
- 不要着急为什么不先说说三种是什么样的锁的?因为我觉得我们更应该先了解三种锁有啥不同,JVM为什么要进行锁升级、降级
- 首先计算机分为用户态、内核态;内核态可以理解成计算机本家人,用户态理解成远方亲戚
- 同样需要操作计算机硬件(钱)的时候,肯定内核态(家人)会有更高的支配权,而用户态(亲戚)只能有很低的支配权。
- 计算机中用户态、内核态切换来完成用户态无权操作的事情。比如说借钱,一个远房亲戚想找你借钱就必须跟你说明情况在打感情牌才能借到钱,这里的你就是感性的。对于远房亲戚来说又得到你身边,又得跟你说明情况,又得写字据,又得软磨硬泡才能达成目的。这一系列下来很实耗费计算机性能。
- 而synchronized的底层就是操作计算机硬件部分。这个操作用户态是无法执行的就涉及了用户态切换内核态的过程。这一个过程就是重量级锁。在计算机的世界里用户态切换成内核态的时间可能已经够执行业务的时间了。所以如果你加上
synchronized
而在70%场景中没有并发但计算机还是每次都切换为内核态,那么总时间可能是2倍的开销。 - 基于这种场景JVM提出了偏向锁、轻量级锁、重量级锁
偏向锁
- 偏向锁是JVM认为没有发生并发的场景下提供的锁。因为在Oracle调研中发现加上
synchronized
关键字表示用户希望并发锁住。但是实际场景中并发场景不是无时无刻的,在70%的时间中可能都没有并发。就算是淘宝也只是双11压力过大,其他时间段不会有双11的并发量级的。所以加了synchronized
关键字如果没有竞争JVM就没必要加上一把重量级锁。所以JVM检测到当前没有并发发生时,synchronized
内部是一把偏向锁。 - 偏向锁是在没有发生竞争的条件下,JVM通过CAS将当前线程写入到markword中,写入成功表示上锁成功。换句话说因为没有竞争,所以cas一定会成功的。至于如何判定没有竞争条件这就是JVM操作的了。
- 在内存分布章节中我们也列出了偏向锁markword是第三位为
101
。 - 在内存分布中我们只是列举出每种锁的标识,细心的你应该发现了在主线程中创建一个对象并锁住这个时候就没有并发,貌似通过JOL分析出来的markword最后三位并不是
101
- 这里的user不就符合偏向锁的场景吗?为什么markword时000 , 根据内存分布章节中我们查出000表示无锁状态。
- 这里我的理解是JVM认为启动初期不会有并发,同时JVM为了性能考虑默认设置4s以后开始对加锁普通对象升级偏向锁。有关参数
-XX:BiasedLockingStartupDelay
- 我们可以通过
java -XX:+PrintFlagsFinal | grep BiasedLock
来查看相关的配置
- 我们能够看到
BiasedLockingStartupDelay=4000
,也就是4S 。 那么我们针对次设置要么修改该参数为0 或者程序中等5S在打印。
- 等待5S后就出现
101
即偏向锁。到这里我们偏向锁的概念就结束了 - 实际上如果细分的话偏向锁还可以分为匿名偏向锁和偏向锁两个概念 。vm配置如下
-XX:+UseBiasedLocking -XX:BiasedLockingStartupDelay=0