Java中的偏向锁、轻量级锁和重量级锁

在 JDK1.6 版本之前,所有的 Java 内置锁都是重量级锁。重量级锁会造成 CPU 在用户态与核心态之间频繁切换,所以代价高、效率低。JDK1.6 版本为了减少获得锁和释放锁所带来的性能消耗,引入了“偏向锁”和“轻量级锁”实现。所以,在 JDK1.6 版本里内置锁一共有四种状态:无锁状态、偏向锁状态、轻量级锁状态、重量级锁状态,这些状态随着竞争情况逐渐升级。内置锁可以升级但不能降级,意味着偏向锁升级成轻量级锁后不能降级成偏向锁。这种能升级却不能降级的策略,其目的是为了提高获得锁和释放锁的效率。

1. 无锁状态

Java 对象刚创建时,还没有任何线程来竞争,说明该对象处于无锁状态(无线程竞争它),这时偏向锁标识位是 0、锁状态 01。无锁状态下对象的 Mark Word 如下图所示。

 

2. 偏向锁状态

偏向锁是指一段同步代码一直被一同个线程所访问,那么该线程会自动获取锁,降低获取锁的代价。如果内置锁处于偏向状态,当有一个线程来竞争锁时,先用偏向锁,表示内置锁偏爱这个线程,这个线程要执行该锁关联的同步代码时,不需要再做任何检查和切换。偏向锁在竞争不激烈的情况下,效率非常高。

偏向锁状态的 Mark Word 会记录内置锁自己偏爱的线程 ID,内置锁会将该线程当做自己的熟人。偏向锁状态下对象的 Mark Word 具体如下图所示。

Java中的偏向锁、轻量级锁和重量级锁_第1张图片

 

3. 轻量级锁状态

当有两个线程开始竞争这个锁对象,情况发生变化了,不再是偏向(独占)锁了,锁会升级为轻量级锁,两个线程公平竞争,哪个线程先占有锁对象,锁对象的 Mark Word 就指向哪个线程的栈帧中的锁记录。轻量级锁状态下对象的 Mark Word 具体如下图所示。

当锁处于偏向锁的时候,而又被另一个线程所企图抢占时,偏向锁就会升级为轻量级锁。企图抢占的线程会通过自旋的形式尝试获取锁,不会阻塞抢锁线程,以便提高性能。

自旋原理非常简单,如果持有锁的线程能在很短时间内释放锁资源,那么那些等待竞争锁的线程就不需要做内核态和用户态之间的切换进入阻塞挂起状态,它们只需要等一等(自旋),等持有锁的线程释放锁后即可立即获取锁,这样就避免用户线程和内核的切换的消耗。

但是线程自旋是需要消耗 CPU 的,如果一直获取不到锁,那线程也不能一直占用 CPU 自旋做无用功,所以需要设定一个自旋等待的最大时间。JVM 对于自旋周期的选择,JDK1.6 之后引入了适应性自旋锁,适应性自旋锁意味着自旋的时间不是固定的,而是由前一次在同一个锁上的自旋时间以及锁的拥有者的状态来决定。线程如果自旋成功了,则下次自旋的次数会更多,如果自旋失败了,则自旋的次数就会减少。

如果持有锁的线程执行的时间超过自旋等待的最大时间扔没有释放锁,就会导致其他争用锁的线程在最大等待时间内还是获取不到锁,自旋不会一直持续下去,这时争用线程会停止自旋进入阻塞状态,该锁膨胀为重量级锁。

4. 重量级锁状态

重量级锁会让其他申请的线程之间进入阻塞,性能降低。重量级锁也就叫做同步锁,这个锁 对象 Mark Word 再次发生变化,会指向一个监视器(Monitor)对象,该监视器对象用集合的形 式,来登记和管理排队的线程。重量级锁状态下对象的 Mark Word 具体如下图所示。

 

你可能感兴趣的:(java,开发语言,jvm)