核心思想:如果程序没有经侦,则取消之前已经获得锁的线程同步操作。也就是说,若某一线程别锁获得后,就会进入偏向模式,当线程再次请求这个锁时,则无需进行相关的同步操作,从而节省了操作时间。如果在此之间有其他线程进行了锁请求,则锁退出偏向模式。在JVM中使用-XX:+UseBiasedLocking可以设置启用偏向锁。
偏向锁在少竞争的情况下,对系统性能有一定帮助。竞争强烈,可以禁用偏向锁
轻量级锁
如果偏向锁失败,Java虚拟机会让线程申请轻量级锁。轻量级锁在虚拟机内部,使用一个称为BasicObjectLock的对象实现,这个对象内部由一个BasicLock对象和一个持有该锁的Java对象指针组成。BasicObjectLock对象放置在Java栈的栈桢中。在BasicLock对象内部还诶护着displaced_header字段,它用于备份对象头部的Mark Word。
由于BasicObjectLock对象在线程栈中,因此该指针必然指向持有该锁的线程栈空间。当需要判断某一线程是否持有该对象锁时,也只需要简单的判断对象头的指针是否在当前线程的栈地址范围内即可。
首先BasicLock通过set_displaced_header()方法备份了原对象的Mark Word。接着,使用CAS操作,尝试将BasicLock的地址复制到对象头的Mark Word。若果复制成果,那么加锁成功,否则认为加锁失败,那么轻量级锁就可能被膨胀为重量级锁。
锁膨胀
当轻量级锁失败,虚拟机就会使用重量级锁。
1、废弃前面BasicLock备份的对象头信息
2、启用重量级锁
首先通过inflate()方法进行锁膨胀,其目的是获取对象的ObjectMonitor
然后使用enter()方法进入该锁
在enter()方法调用中,线程在操作系统层面被刮起。如果这样,线程间的切换和调度的成本就会比较高
自旋锁
可以使线程在没有获取锁时,不被挂起,而转而去执行一个空循环,在若干空循环后,线程若果可以获得锁,则继续执行,若线程依然不能获取锁,则被挂起
1.7中:参数被取消,虚拟机不再支持由用户配置自旋锁。自旋锁总是会执行,自旋次数也是由虚拟机自行调整
锁消除
Java虚拟机在JIT编译时,通过对运行上下文扫描,去除不可能存在共享资源竞争的锁。通过锁消除,可以节省毫无意义的请求锁时间
逃逸分析和锁消除 通过参数开启。必须在- server模式下
减少锁持有时间
减小锁粒度
锁分离
是减小锁粒度的一个特例,LinkBlockingQueue的实现,通过takeLock和putlock两把锁,实现了取数据和写数据的分离
锁粗化
虚拟机在遇到一连串地对同一锁不断进行请求和释放的操作时,便会把所有的锁操作整合成对锁的一次请求,从而减少对锁的请求同步次数。这个操作叫做锁的粗化