锁-优化篇
适应自旋锁:自旋时间不固定
- 在Java中,自旋锁是指尝试获取锁的线程不会立即阻塞,而是采用循环的方式去尝试获取锁,这样的好处是减少线程上下文切换的消耗,缺点是循环会消耗CPU。
- 锁竞争是kernal mode下的,会经过user mode(用户态)到kernal mode(内核态) 的切换,是比较花时间的。
- 自旋锁出现的原因是人们发现大多数时候锁的占用只会持续很短的时间,甚至低于切换到kernal mode所花的时间,所以在进入kernal mode前让线程等待有限的时间,如果在此时间内能够获取到锁就避免了很多无谓的时间,若不能则再进入kernal mode竞争锁。
- 在JDK 1.6中引入了自适应的自旋锁,说明自旋的时间不固定,要不要自旋变得越来越聪明。
- 自旋锁在JDK1.4.2中就已经引入,只不过默认是关闭的,可以使用-XX:+UseSpinning参数来开启,在JDK1.6中就已经改为默认开启了。
锁消除:如果发现代码是线程安全的,将锁去掉
如果JVM明显检测到某段代码是线程安全的(言外之意:无锁也是安全的),JVM会安全地原有的锁消除掉!
例如:
Vector是默认加锁的,但JVM如果发现vector变量仅仅在vectorTest()方法中使用,那该vector是线程安全的。JVM会把vector内部加的锁去除,这个优化就叫做:锁消除。
public static void vectorTest() { Vector<String> vector = new Vector<String>(); for (int i = 0; i < 10; i++) { vector.add(i + ""); } System.out.println(vector); }
锁粗化:加锁范围过小(重复加锁),将加锁的范围扩展
- 默认情况下,总是推荐将同步块的作用范围限制得尽量小。
- 但是如果一系列的连续操作都对同一个对象反复加锁和解锁,甚至加锁操作是出现在循环体中的,频繁地进行互斥同步操作也会导致不必要的性能损耗。
- JVM会将加锁的范围扩展(粗化),这就叫做锁粗化。
轻量级锁:在无竞争的情况下使用CAS操作去消除同步使用的互斥量
- 轻量级锁能提升程序同步性能的依据是“对于绝大部分的锁,在整个同步周期内都是不存在竞争的”,这是一个经验数据。
- 如果没有竞争,轻量级锁使用CAS操作避免了使用互斥量的开销但如果存在锁竞争,除了互斥量的开销外,还额外发生了CAS操作,因此在有竞争的情况下,轻量级锁会比传统的重量级锁更慢。
- 简单来说:如果发现同步周期内都是不存在竞争,JVM会使用CAS操作来替代操作系统互斥量。这个优化就被叫做轻量级锁。
偏向锁:在无竞争环境下,把整个同步都消除,CAS也不做。
- 偏向锁就是在无竞争的情况下把整个同步都消除掉,连CAS操作都不做了!
- 偏向锁可以提高带有同步但无竞争的程序性能。它同样是一个带有效益权衡(Trade Off)性质的优化,也就是说,它并不一定总是对程序运行有利,如果程序中大多数的锁总是被多个不同的线程访问,那偏向模式就是多余的。在具体问题具体分析的前提下,有时候使用参数-XX:-UseBiasedLocking来禁止偏向锁优化反而可以提升性能。