轻量锁 偏向锁
来源:互联网 发布:现代软件学院马少红 编辑:程序博客网 时间:2024/04/29 15:11
Java偏向锁(Biased Locking)是Java6引入的一项多线程优化。它通过消除资源无竞争情况下的同步原语,进一步提高了程序的运行性能。
如果在运行过程中,遇到了其他线程抢占锁,则持有偏向锁的线程会被挂起,JVM会尝试消除它身上的偏向锁,将锁恢复到标准的轻量级锁。(偏向锁只能在单线程下起作用)
第二步,如果MarkWord不是自己的ThreadId,锁升级,这时候,用CAS来执行切换,新的线程根据MarkWord里面现有的ThreadId,通知之前线程暂停,之前线程将Markword的内容置为空。
第三步,两个线程都把对象的HashCode复制到自己新建的用于存储锁的记录空间,接着开始通过CAS操作,把共享对象的MarKword的内容修改为自己新建的记录空间的地址的方式竞争MarkWord,
第四步,第三步中成功执行CAS的获得资源,失败的则进入自旋
第五步,自旋的线程在自旋过程中,成功获得资源(即之前获的资源的线程执行完成并释放了共享资源),则整个状态依然处于 轻量级锁的状态,如果自旋失败
轻量锁
http://blog.sina.com.cn/s/blog_c038e9930102v2ht.html
轻量级锁也是一种多线程优化,它与偏向锁的区别在于,轻量级锁是通过CAS来避免进入开销较大的互斥操作,而偏向锁是在无竞争场景下完全消除同步,连CAS也不执行(CAS本身仍旧是一种操作系统同步原语,始终要在JVM与OS之间来回,有一定的开销)。
轻量级锁(LightweightLocking)本意是为了减少多线程进入互斥的几率,并不是要替代互斥。
它利用了CPU原语Compare-And-Swap(CAS,汇编指令CMPXCHG),尝试在进入互斥前,进行补救。
轻量级锁加锁:线程在执行同步块之前,JVM会先在当前线程的栈桢中创建用于存储锁记录的空间,并将对象头中的MarkWord复制到锁记录中,官方称为DisplacedMark Word。然后线程尝试使用CAS将对象头中的MarkWord替换为指向锁记录的指针。如果成功,当前线程获得锁,如果失败,表示其他线程竞争锁,当前线程便尝试使用自旋来获取锁。
轻量级锁解锁:轻量级解锁时,会使用原子的CAS操作来将DisplacedMark Word替换回到对象头,如果成功,则表示没有竞争发生。如果失败,表示当前锁存在竞争,锁就会膨胀成重量级锁。下图是两个线程同时争夺锁,导致锁膨胀的流程图。
这个displacedmark word是整个轻量级锁实现的关键,在CAS中的compare就需要用它作为条件。
为什么要拷贝mark word?其实很简单,原因是为了不想在lock与unlock这种底层操作上再加同步。
在拷贝完object markword之后,JVM做了一步交换指针的操作,即流程中第一个橙色矩形框内容所述。
============
将object mark word里的轻量级锁指针指向lockrecord所在的stack指针,作用是让其他线程知道,该objectmonitor已被占用(就像偏向锁中用CAS的方式将markword的id指向当前尝试获取锁的线程id,这里是将markword中的轻量级锁指针以CAS的方式尝试指向当前线程的lockrecord,这样别的线程便知道当前轻量锁已经指向别的线程了)。
lock record里的owner指针指向objectmark word的作用是为了在接下里的运行过程中,识别哪个对象被锁住了。
CAS 的是makeworld中的轻量级锁指针
================
下图直观地描述了交换指针的操作。
最后一步unlock中,我们发现,JVM同样使用了CAS来验证object markword在持有锁到释放锁之间,有无被其他线程访问。
如果其他线程在持有锁这段时间里,尝试获取过锁,则可能自身被挂起,而markword的重量级锁指针也会被相应修改。
释放锁线程视角:所以由轻量锁切换到重量锁,是发生在轻量锁释放锁的期间,之前在获取锁的时候它拷贝了锁对象头的markword,在释放锁的时候如果它发现在它持有锁的期间有其他线程来尝试获取锁了,并且该线程对markword做了修改,两者比对发现不一致,则切换到重量锁。
因为重量级锁被修改了,所有display mark word和原来的markword不一样了。
怎么补救,就是进入mutex前,compare一下obj的markword状态。确认该markword是否被其他线程持有。
此时如果线程已经释放了markword,那么通过CAS后就可以直接进入线程,无需进入mutex,就这个作用。
尝试获取锁线程视角:如果线程尝试获取锁的时候,轻量锁正被其他线程占有,那么它就会修改markword,修改重量级锁,表示该进入重量锁了。
还有一个注意点:等待轻量锁的线程不会阻塞,它会一直自旋等待锁,并如上所说修改markword。
这就是自旋锁,尝试获取锁的线程,在没有获得锁的时候,不被挂起,而转而去执行一个空循环,即自旋。在若干个自旋后,如果还没有获得锁,则才被挂起,获得锁,则执行代码。
因为自旋会消耗CPU,为了避免无用的自旋(比如获得锁的线程被阻塞住了),一旦锁升级成重量级锁,就不会再恢复到轻量级锁状态。当锁处于这个状态下,其他线程试图获取锁时,都会被阻塞住,当持有锁的线程释放锁之后会唤醒这些线程,被唤醒的线程就会进行新一轮的夺锁之争。
- 轻量锁 偏向锁
- 锁原理:偏向锁、轻量锁、重量锁
- Java偏向锁
- Java 偏向锁测试
- java 偏向锁
- 偏向锁-学习
- jvm第7节-锁(偏向锁,轻量锁,自旋锁)
- 轻量级锁与偏向锁
- 偏向锁与轻量级锁
- 偏向锁和轻量级锁
- 轻量级锁和偏向锁
- java虚拟机的偏向锁
- java 偏向锁优化技术
- 自旋锁、阻塞锁、重入锁、偏向锁、轻量锁和重量锁
- 自旋锁、阻塞锁、重入锁、偏向锁、轻量锁和重量锁
- 偏向锁,轻量级锁,重量级锁
- 偏向锁,轻量级锁,重量级锁
- 对象监视器锁、轻量级锁、偏向锁
- Maven系列--pom.xml 配置详解
- 6174问题
- 关于最短路径算法的理解
- Sublime text2、Git、Github三者协同
- RPM方式安装MySQL5.6
- 轻量锁 偏向锁
- QTP 无法识别web 大全
- 计算机网络(二)
- springMVC中ajax的使用方法总结。
- DynamicLoadApk 源码解析
- 使用HttpURLConnection访问网络
- Android开发那些事
- 如何遍历root下的所有的child
- beaglebone-black使用TI-sdk 通过修改dts文件来使能 spidev1.0