Java虚拟机对锁(synchronize)的优化:锁消除、锁粗化、偏向锁、自旋锁

143 阅读3分钟

本文已参与「新人创作礼」活动, 一起开启掘金创作之路。

在jdk1.6之后的版本,Java虚拟机对锁进行了优化。

锁消除、锁粗化、偏向锁、自旋锁 这些指的都不是一个实实在在的锁,而是指的是synchroized加锁的方式的不同。

\

锁消除:JIT编译器对加锁的一种优化。JIT编译器在进行上下文扫描的时候,会去除一些不存在竞争的锁。JIT通过逃逸分析技术,确定某个锁不存在竞争关系。编译器就会对锁进行消除,避免不必要的加锁开销,提高性能。

锁粗化:JIT编译器在进行扫描的时候,发现存在连续串行加锁的情况,就会将这段代码合成一个锁,避免重复加锁。

偏向锁:大部分情况是没有竞争的,所以可以通过偏向来提高性能。只有一个线程获取锁,没有其他线程。

所谓的偏向,就是偏心,即锁会偏向于当前已经占有锁的线程(也就是说,这个线程已经占有这个锁,当他在次试图去获取这个锁的时候,他会已最快的方式去拿到这个锁,而不需要在进行一些monitor操作,因此这方面他是会对性能有所提升的,因为在大部分情况下是没有竞争的,所以锁此时是没用的,所以使用偏向锁是可以提高性能的)

在使用偏向锁的时候会将对象头Mark的标记设置为偏向,并将拿到锁的线程的ID写入对象头Mark,这样就可以很快识别出这个线程是否拿到的锁。

只要没有竞争,获得偏向锁的线程,在将来进入同步块,不需要做同步(这段时间就省下来了)

当其他线程请求相同的锁时,偏向模式结束

轻量级锁: 轻量级锁是由偏向锁升级而来,当存在第二个线程申请同一个锁对象时,偏向锁就会立即升级为轻量级锁。注意这里的第二个线程只是申请锁,不存在两个线程同时竞争锁,可以是一前一后地交替执行同步块。AB两个线程同时通过CAS来进行加锁。谁先将对象头中Mark Word中的Lock Word 指向自己的线程ID,那么谁就会获取到这个锁。

在竞争激烈的场合,偏向锁会增加系统负担

自旋锁: 自旋锁在jdk1.4的时候就已经出现,但是需要增加jvm参数才能开启。

自旋锁指的是,一个线程在加锁的过程中,如果没有加锁成功,就会进行一个while空循环,空循环的作用就是等待一把锁。但是不能一直等待,在等待了固定的次数后,还没有获取到锁,才会把该线程挂起。这样做的好处,为了减少上下文切换的开销,因为如果获取不到锁,马上就挂起,当这个锁释放的速度很快,就会导致频繁的上下文切换。导致不必要的性能开销。

自适应自旋锁在jdk1.6后,对自旋锁进行了优化,自旋锁默认开启,不再需要另外的jvm参数。自旋锁空循环获取锁的次数不再固定,而是由上一次加锁等待的次数增加几次来确定。比如说,上一次加锁需要等待10次,那么这次加锁,很有可能只需要在原有的基础上增加一两次就可以获取到锁。由jvm自己来确定自旋的次数。叫做自适应自旋锁。