偏向锁是JDK1.6提出来的一种锁优化的机制。其核心的思想是,若是程序没有竞争,则取消以前已经取得锁的线程同步操做。也就是说,若某一锁被线程获取后,便进入偏向模式,当线程再次请求这个锁时,就无需再进行相关的同步操做了,从而节约了操做时间,若是在此之间有其余的线程进行了锁请求,则锁退出偏向模式。在JVM中使用-XX:+UseBiasedLockingjava
package jvmProject; import java.util.List; import java.util.Vector; public class Biased { public static List<Integer> numberList = new Vector<Integer>(); public static void main(String[] args) { long begin = System.currentTimeMillis(); int count = 0; int startnum = 0; while(count<10000000){ numberList.add(startnum); startnum+=2; count++; } long end = System.currentTimeMillis(); System.out.println(end-begin); } }
初始化一个Vector,往里面添加10000000个Integer对象,而后输出时间差。以此来测试偏向锁的性能。至于为何要使用Vector而不使用ArrayList呢?安全
由于ArrayList是线程不安全的,Vector是线程安全的。这样说可能还不够具体,能够翻看一下源码吧。并发
Vector中的几乎全部操做是带有sychronized的,而ArrayList是没有的,因此Vector是线程安全的。jvm
接下来咱们来测试一下,开启偏向锁和不开启偏向锁对程序性能的影响有多大。性能
配置JVM启动(开启偏向锁)参数为:测试
配置JVM启动(关闭偏向锁)参数为:优化
Perfect!开启偏向锁的程序运行时间明显较短,开启偏向锁比不开启偏向锁,在单个线程中操做一个对象的同步方法,是有必定的优点的。其实也能够这样理解,当只有一个线程操做带有同步方法的Vector对象的时候,此时对Vector的操做就转变成了对ArrayList的操做。spa
偏向锁在锁竞争激烈的场合没有太强的优化效果,由于大量的竞争会致使持有锁的线程不停地切换,锁也很难保持在偏向模式,此时,使用偏向锁不只得不到性能的优化,反而有可能下降系统的性能,所以,在激烈竞争的场合,能够尝试使用操作系统
-XX:-UseBiastedLocking参数禁用偏向锁。线程
若是偏向锁失败,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字段则指向持有锁的对象头部。
当轻量级锁失败,虚拟机就会使用重量级锁。在使用重量级锁的时,对象的Mark Word以下:
[ptr |10] monitor
重量级锁在操做过程当中,线程可能会被操做系统层面挂起,若是是这样,线程间的切换和调用成本就会大大提升。
自旋锁可使线程在没有取得锁的时候,不被挂起,而转去执行一个空循环,(即所谓的自旋,就是本身执行空循环),若在若干个空循环后,线程若是能够得到锁,则继续执行。若线程依然不能得到锁,才会被挂起。
使用自旋锁后,线程被挂起的概率相对减小,线程执行的连贯性相对增强。所以,对于那些锁竞争不是很激烈,锁占用时间很短的并发线程,具备必定的积极意义,但对于锁竞争激烈,单线程锁占用很长时间的并发程序,自旋锁在自旋等待后,每每毅然没法得到对应的锁,不只仅白白浪费了CPU时间,最终仍是免不了被挂起的操做 ,反而浪费了系统的资源。
在JDK1.6中,Java虚拟机提供-XX:+UseSpinning参数来开启自旋锁,使用-XX:PreBlockSpin参数来设置自旋锁等待的次数。
在JDK1.7开始,自旋锁的参数被取消,虚拟机再也不支持由用户配置自旋锁,自旋锁老是会执行,自旋锁次数也由虚拟机自动调整。