java虚拟机笔记(十四)java锁优化

锁优化
jdk5升级到jdk6 花了大量的精力去实现各种锁优化技术:
自旋锁,自适应自旋,锁消除,锁膨胀,轻量级锁,偏向锁

1.自旋锁
互斥同步对性能最大的影响是阻塞,挂起线程和恢复线程都需要上下文切换, 但大多数时候 共享变量的锁定只会持续很短的时间,
因此让想获取锁的线程,先自旋等待一段时间,而不是进入阻塞。
缺点:自旋需要占用处理器的时间,如果自旋时间很短 那么很划得来,但是长时间自旋,就浪费性能,因此自旋不能代替阻塞。

jdk6引入了自适应自旋,自旋时间根据上一次的获取锁的过程来决定,如果之前自旋很短的时间就能获取锁,那么就倾向于
这次也自旋长一点时间。

2.锁消除
对不可能存在竞争的锁进行消除,判断的依据是根据逃逸分析技术, 逃逸分析发现这部分的数据没有被暴露出去给外部访问到,那么就不会存在竞争,如果存在锁的话,就可以消除掉。
比如说 s1+s2+s3 ,如果编译后被优化成Stringbuff 的三个append, 但是append内部是用synchronized修饰的
如果这个变量没有逃逸出方法之外,那么这个锁可以消除掉。 (一般是优化成不加锁的StringBuild)

3.锁粗化
检测到当一系列操作都对同一个对象加锁,比如说加锁出现在循环体之中,那即时是没有线程竞争,频繁的加锁也会增加性能的损耗
,因此会把锁的适当范围扩大。

4.轻量级锁
设计的目的是在没有多线程竞争的情况下,减少重量级锁使用互斥同步带来的性能损耗,并不是用来代替重量级锁的。
实现原理:
hotspot虚拟机对象的内存布局,分为对象头和实例数据
对象头又分为markword和类型指针,如果是数组,还会存一个长度。
markword用于存储对象自身运行时的数据,比如说哈希码hashcode,GC分代年龄等。 markword在32位虚拟机上占32位,在64位虚拟机上占64位。

对象头是用来存储与对象自身定义数据无关的状态数据,因此为了节省空间,设计成动态数据结构。
会根据不同的状态存储不同的数据,这样可以复用之前的空间。
java虚拟机笔记(十四)java锁优化_第1张图片

轻量级锁的过程:
代码即将进入同步块的时候,如果同步对象没有被锁定(锁标志位位01),虚拟机首先在当前栈帧中建立一个锁记录
lock record 空间,用于存储对象目前markword的拷贝 ,会加一个displaced前缀,也叫displaced markword
然后虚拟机使用cas操作尝试将对象的markword更新成指向Lock record的指针,如果更新成功,即表示该线程
拥有了该对象的锁,锁标志转为00 。

如果更新失败,cas (markword的地址,markword的值,指向lock record的指针) , 说明原来的markword已经改变了,
那么去看这个markword是否指向当前线程的lock record ,如果是 说明这个线程已经获取到了锁,直接进入同步块执行。
如果不是,说明被其他线程获取到了锁,出现了两条以上的线程竞争该锁,此时轻量级锁就会膨胀成重量级锁 ,锁状态变为10.
markword中存储的就是指向重量级锁的指针。 后面等待锁的线程也要进入阻塞。

解锁,使用cas操作(markWord的地址,lock record指针,displaced markword值) 将原本存储在lock record中的Mark word 替换回来。替换成功则解锁成功。如果替换失败,说明lock record指针已经改变,有其他线程竞争过该锁,已经膨胀成了重量级锁,
那么释放锁的同时(根据指向重量级锁的指针去找到monitor 并释放),还需要唤醒等待的线程。

在没有竞争的情况下,轻量级锁通过cas避免了重量级锁使用互斥量的开销,但是在有竞争的情况下,除了使用互斥量的开销
还有使用cas的开销。

偏向锁:
目的是消除在无竞争情况下的同步,将整个同步都消除掉。
轻量级锁是消除无竞争情况下的互斥量开销,偏向锁连同步都消除了。

原理:
偏向锁倾向于第一个获取到这个锁的线程,如果接下来该锁一直没有被其他线程获取
则持有偏向锁的线程不需要再同步。

流程:
当锁对象第一次被线程获取时,虚拟机会将对象头中的标志位设置为01,把偏向模式设置为1,表示进入偏向模式。
同时用cas操作将这个锁的线程ID记录到markword中,如果cas操作成功,那么持有偏向锁的线程以后进入这个锁相关的同步块
都不再需要进行任何同步操作。

一旦有其他线程尝试去获取这个锁,偏向模式就立刻结束,根据锁对象是否处于被锁定状态决定是否撤销偏向锁(撤销就是将偏向锁状态设置为0),撤销之后标志位恢复到未锁定(01) 或者轻量级锁(00) 状态。 后续就按照轻量级锁进行。

需要注意的是:
1.当一个对象已经计算过hashcode之后(使用object:hashcode方法 重写的不算),就无法进入偏向锁状态,如果正处于偏向锁状态,又收到需要计算一致性状态的时候,偏向状态会被立刻撤销,并膨胀成重量级锁。(重量级锁指针指向一个Objectmonitor对象,里面有字段可以存markWord)
2.偏向锁也并非万能的,如果在多个线程必定竞争的场景,偏向锁就成了多余的,关掉反而性能会更好。

锁膨胀是指 锁升级的过程。 锁粗化是指锁的范围扩大
以上都是对synchronized的优化,特别是偏向锁 轻量级锁。

java虚拟机笔记(十四)java锁优化_第2张图片

你可能感兴趣的:(java虚拟机)