优化思路导图
虚拟机层面
- 偏向锁
偏向锁:即锁会偏向于当前已经占有锁的线程。同步块大多数情况下不会出现多线程同时竞争锁,在无竞争时,之前获得锁的线程再次获取锁时,会判断是否偏向锁指向我,那么该线程将不用再次获得锁,直接进入同步块。
偏向锁的实施是将对象头Mark的标记设置为偏向,并将线程ID写入对象头Mark。JVM默认启用偏向锁 -XX:+UseBiasedLocking。当其他线程请求相同的锁时,偏向模式结束。因此在竞争激烈的场合,偏向锁反而会增加系统负担,因为每一次都要判断是否偏向。 - 轻量级锁
Java的多线程安全是基于Lock机制实现的,但Lock的性能往往不如人意。原因是monitorenter与monitorexit这两个控制多线程同步的bytecode原语,是JVM依赖操作系统互斥(mutex)来实现的。互斥是一种会导致线程挂起,并在较短时间内需重新调度回原线程,很耗费资源的操作。
为了优化Java的Lock机制,从Java 6开始便引入了轻量级锁的概念。轻量级锁利用了CPU原语Compare-And-Swap(CAS,汇编指令CMPXCHG)尝试在进入互斥前进行补救,如果偏向锁失败,系统会进行轻量级锁的操作。
总结一下:轻量级锁是一种快速的锁定方法,在进入互斥之前,使用CAS操作来尝试加锁,尽量不用操作系统层面的互斥,提高性能。 - 自旋锁
当竞争存在时,轻量级锁尝试失败,有可能会直接升级成重量级锁动用操作系统层面的互斥,也可能会尝试一下自旋锁。
如果线程可以快速获取锁,可以不再OS层挂起,而让线程多做几个空操作(自旋),并且多次尝试获取这个锁。当循环次数达到后,仍然升级成重量级锁。JDK1.6中:-XX:+UseSpinning开启,JDK1.7中:去掉此参数,修改为内置实现。
如果同步块很长,自旋失败,系统性能会下降。相反,当同步块较短,自旋成功时,系统性能会上升。
总结
偏向锁、轻量级锁及自旋锁是内置在JVM当中的乐观锁实现,不是Java语言层面的锁优化方法。
偏向锁:是为了避免某个线程反复获得/释放同一把锁时的性能消耗,如果仍然是同个线程去获得这个锁,尝试偏向锁时会直接进入同步块,不需要再次获得锁。
轻量级锁和自旋锁:是为了避免直接调用操作系统层面的互斥操作,因为挂起线程是一个很耗资源的操作。为了尽量避免使用重量级锁,首先会尝试轻量级锁,轻量级锁会尝试使用CAS操作来获得锁,如果轻量级锁获取失败,说明存在竞争。但也许能很快获得锁,就会尝试自旋锁,将线程做几个空循环,每次循环时都不断尝试获取锁。如果自旋锁也是白,那只能升级成重量级锁。