JVM笔记

21.锁优化

2019-12-11  本文已影响0人  雪关马不前

自旋锁

自旋锁是采用让当前线程不停地的在循环体内执行实现的,当循环的条件被其他线程改变时 才能进入临界区。

自适应自旋

优化:在jdk1.6中引入了自适应的自旋锁。自适应意味着自旋的时间不再固定了,而是由前一次在同一个锁上的自旋时间及锁的拥有者的状态来决定。如果同一个锁对象上,自旋等待刚刚成功获得过锁,并且持有锁的线程正在运行中,那么虚拟机就会认为这次自旋也很有可能再次成功,进而它将允许自旋等待持续相对更长的时间。反之,相反。

锁消除

锁消除是指虚拟机即使编译器在运行时,对一些代码上要求同步,但是被检测到不可能存在共享数据竞争的锁进行消除。锁消除主要判定依据来源于逃逸分析的数据支持,如果判断在一段代码中,堆上的所有数据都不会逃逸出去从而被其他线程访问到,那就可以把它们当作栈上的数据对待,认为是线程私有的,同步加锁自然就无须进行。

锁消除Demo


public String concatString(String s1,String s2,String s3){
    return s1+s2+s3;
}
//Javac转化后的字符串连接操作
public String concatString(String s1,String s2,String s3){
    StringBuffer sb= new StringBuffer();
    sb.append(s1);
    sb.append(s2);
    sb.append(s3);
    return sb.toString();
}

在这段代码中每个StringBuffer.append()方法中都有一个同步块,锁就是sb对象。虚拟机观察变量sb,很快就发现它的动态作用域被限制在concatString()方法内部。也就是说,sb的所有引用永远不会逃逸到concatString()方法之外。其他线程无法访问到它,因此,虽然这里有锁,但是可以被安全消除掉。

锁粗化

简单的来说就是用户频繁对同一个对象进行加锁或者解锁,会导致锁粗化。比如:在一个循环里面对同一个对象进行加锁和解锁,就可能会把对这个对象的加锁提前到循环之前。

轻量级锁

轻量级锁是通过虚拟机的对象(对象头部分)来实现的,HotSpot虚拟机的对象头分为两部分信息,第一部分用于存储对象自身的运行数据,如HashCode,GC分代龄等,这部分数据的长度在32位和64位的虚拟机中分别为32bit和64bit,官方称它为“Mark Word
”,它是实现轻量级锁和偏向锁的关键。另外一部分用于存储指向方法区对象类型数据的指针,如果是数据对象的话,还会有一个额外的部分用于存数组长度。

image

在代码进入同步块的时候,如果此同步对对象没有被锁定,虚拟机首先将在当前线程的栈帧中建立一个名为锁记录(Lock Record)的空间,用于存储锁对象目前的Mark Word的拷贝。接着,虚拟机将使用CAS操作尝试将对象的Mark Word的锁标志位(Mark Word的最后2bit)将转变为”00”,即表示此对象处于轻量级锁定状态。

如果CAS操作更新失败了。虚拟机首先会检查对象的Mark Word是否指向当前线程的栈帧,如果只说明当前线程已经拥有了这个对象的锁,那就可以直接进入同步块继续执行,否则说明这个锁对象已经被其他线程抢占了。如果有两条以上的线程争用同一个锁,那轻量级锁就不再有效,会膨胀微重量级锁。锁标志的状态值变为“10”,Mark Word中存储的就是指向重量级锁(互斥量)的指针,后面等待锁的线程也要进入阻塞状态。

偏向锁

偏向锁是JDK1.6中引入的一项锁优化,它的目的是消除数据在无竞争情况下的同步原语,进一步提高程序的运行性能。如果说轻量级锁是在无竞争的情况下使用CAS操作去消除同步使用的互斥量,那偏向锁就是在无竞争的情况下把整个同步都消除掉,连CAS操作都不做了。

当锁对象第一次被线程获取的时候,虚拟机将会把对象头的标志位设为“01”,即偏向模式。同时使用CAS操作把获取到这个锁的线程的ID记录在对象的Mark Word之中,如果CAS操作成功,持有偏向锁的线程以后每次进入这个锁相关的同步块时,虚拟机都可以不再进行任何同步操作(Locking,Unlocking以及Mark Word的Update等)。

当有另一个线程去尝试获取这个锁时,偏向锁模式就宣告结束。根据对象目前是否处于被锁的状态,撤销偏向后恢复到未锁定(标志位为“01”)或轻量级锁定(标志位为“00")的状态,后续的同步操作就和轻量级锁那样执行。

image
上一篇下一篇

猜你喜欢

热点阅读