自旋锁,偏向锁,轻量级锁,重量级锁

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了自旋锁,偏向锁,轻量级锁,重量级锁相关的知识,希望对你有一定的参考价值。

参考技术A JVM为了性能,对内置锁做了优化,比如内置锁的分配和膨胀
内置锁只能沿着偏向锁,轻量级锁,重量级锁的顺序逐渐升级,不能降级。

为了减少线程切换(线程挂起和恢复线程都会引起CPU用户态和内核态的切换)的开销,所以产生了自旋锁。

当线程竞争锁失败后,会通过自旋(如空等待)一会,自选的过程中也会竞争锁。如果这个过程没有竞争到锁,那么就会阻塞自己。

注:单核处理器的情况下,是伪并行,只可能存在一个线程执行,所以自旋锁是无意义的。
-XX:-UseSpinning参数关闭自旋锁优化;-XX:PreBlockSpin参数修改默认的自旋次数。

即自旋的时间由前一次这个锁的自旋时间决定:同一个锁对象上,如果上次自旋获得过锁,那么虚拟机允许它自旋等待持续更长的时间;如果很少成功获取锁,那么就会减少自旋时间。

偏向锁:无实际竞争,只有第一个申请锁的线程会使用锁
1.偏向锁会偏向第一个获得它的线程。
2.当线程获取锁的时候,先检查Mark Word是否是可偏向锁的状态。是则检查Mark Word中存储的线程是否为当前线程Id,是则获取成功
3.如果不是,则通过CAS去将其修改成本线程Id,修改成功则一切顺利,失败就会在拥有该锁的线程达到安全点(指当前时间没有字节码正在执行)之后,挂起该线程,升级为轻量级锁。

1.当有线程膨胀为轻量级锁之后,其他线程来竞争锁,虚拟机会在当前线程的栈帧中创建一个锁记录的空间,存储锁对象的Mark Word的拷贝。并拷贝
2.虚拟机使用CAS操作尝试将对象的Mark word更新为指向锁记录的指针,成功,那么该线程就拥有了该对象的锁
3.失败轻量级锁就会膨胀为重量级锁。

偏向锁轻量级锁和自旋锁

偏向锁

偏向锁是JDK1.6提出来的一种锁优化的机制。其核心的思想是,如果程序没有竞争,则取消之前已经取得锁的线程同步操作。也就是说,若某一锁被线程获取后,便进入偏向模式,当线程再次请求这个锁时,就无需再进行相关的同步操作了,从而节约了操作时间,如果在此之间有其他的线程进行了锁请求,则锁退出偏向模式。

轻量级锁

如果偏向锁失败,Java虚拟机就会让线程申请轻量级锁,轻量级锁在虚拟机内部,使用一个成为BasicObjectLock的对象实现的,这个对象内部由一个BasicLock对象和一个持有该锁的Java对象指针组成。BasicObjectLock对象放置在Java栈帧中。在BasicLock对象内部还维护着displaced_header字段,用于备份对象头部的Mark Word.

当一个线程持有一个对象的锁的时候,对象头部Mark Word信息如下

[ptr                       |00] locked  

末尾的两位比特为00,整个Mark Word为指向BasicLock对象的指针。由于BasicObjectLock对象在线程栈中,因此该指针必然指向持有该锁的线程栈空间。当需要判断一个线程是否持有该对象时,只需要简单地判断对象头的指针是否在当前线程的栈地址范围即可。同时,BasicLock对象的displaced_header,备份了原对象的Mark word内容,BasicObjectLock对象的obj字段则指向持有锁的对象头部。

自旋锁

自旋锁可以使线程在没有取得锁的时候,不被挂起,而转去执行一个空循环,(即所谓的自旋,就是自己执行空循环),若在若干个空循环后,线程如果可以获得锁,则继续执行。若线程依然不能获得锁,才会被挂起。

使用自旋锁后,线程被挂起的几率相对减少,线程执行的连贯性相对加强。因此,对于那些锁竞争不是很激烈,锁占用时间很短的并发线程,具有一定的积极意义,但对于锁竞争激烈,单线程锁占用很长时间的并发程序,自旋锁在自旋等待后,往往毅然无法获得对应的锁,不仅仅白白浪费了CPU时间,最终还是免不了被挂起的操作 ,反而浪费了系统的资源。

以上是关于自旋锁,偏向锁,轻量级锁,重量级锁的主要内容,如果未能解决你的问题,请参考以下文章

一句话撸完重量级锁自旋锁轻量级锁偏向锁悲观乐观锁等

偏向锁轻量级锁和自旋锁

Synchronized的原理及自旋锁,偏向锁,轻量级锁,重量级锁的区别(摘抄和理解)

Java锁---偏向锁轻量级锁自旋锁重量级锁

synchronized的锁升级(偏向锁,自旋锁(cas),重量级锁)

自旋锁阻塞锁可重入锁悲观锁乐观锁读写锁偏向所轻量级锁重量级锁锁膨胀对象锁和类锁