JVM锁优化 - omigaw/spring- GitHub Wiki
JDK1.6对锁的实现引入了大量的优化,包括自旋锁、适应性自旋锁、锁消除、锁粗化、偏向锁、轻量级锁等技术。
重量级锁
重量级锁,是JDK1.6之前,内置锁的实现方式。简单来说,重量级锁就是采用互斥量来控制对互斥资源的访问。 历史回顾:在JDK1.6以前的版本,synchronized实现的内置锁都比较重。JVM中monitorenter和monitorexit字节码依赖于底层的操作系统的Mutex Lock来实现的,但是由于使用Mutex Lock需要将当前线程挂起并从用户态切换到内核态来执行,这种切换的代价是非常昂贵的。然而在现实中的大部分情况下,同步方法是运行在单线程环境(无锁竞争环境),如果每次都调用Mutex Lock那么将严重的影响程序的性能。
自旋锁
线程的阻塞和唤醒需要CPU从用户态转为核心态,频繁的阻塞和唤醒对CPU来说是一件负担很重的工作,势必会系统的并发性能带来很大的压力。同时,我们发现在许多应用上面,对象锁的锁状态只会持续很短的一段时间,为了这一段很短的时间频繁地阻塞和唤醒线程是非常不值得的,所以引入自旋锁。 所谓自旋锁,就是让该线程等待一段时间,不会被立即挂起,看持有锁的线程是否会很快释放锁。怎么等待呢?执行一段无意义的循环即可(自旋)。
适应自旋锁
JDK1.6引入了更加聪明的自旋锁,即自适应自旋锁。所谓自适应就意味着自旋的次数不再是固定的,它是由前一次在同一个锁上的自旋时间及锁的拥有者的状态来决定。 线程如果自旋成功了,那么下次自旋的次数会更加多,因为虚拟机认为既然上次成功了,那么此次自旋也很有可能会再次成功,那么它就会允许自旋等待持续的次数更多。反之,如果对于某个锁,很少有自旋能够成功的,那么在以后要获得这个锁的时候自旋的次数会减少甚至省略掉自旋的过程,以免浪费处理器资源。 有了自适应自旋锁,随着程序运行和性能监控信息的不断完善,虚拟机对程序锁的状况预测会越来越准确,虚拟机会变得越来越聪明。
锁消除
为了保证数据的完整性,我们在进行操作时需要对这部分的操作进行同步控制,但是在有些情况下,JVM检测到不可能存在共享数据竞争,这时JVM会对这些同步锁进行锁消除。
锁粗化
就是将多个连续的加锁、解锁操作连接在一起,扩展成一个范围更大的锁。
偏向锁
在采用偏向锁时,如果一个线程第一次来访问互斥资源,则在对象头和栈帧的锁记录中存储偏向锁的线程ID。偏向锁在获取锁之后,直到有竞争出现才会释放锁。也就是说,如果长期没有竞争,偏向锁是一直持有锁的。这样,当线程下次再次进入同步块的时候就不需要进行任何获取锁的操作,即可访问互斥资源。节约了频繁获取锁和释放锁的开销。
轻量级锁
重量级锁之所以开销大,关键是其存在线程上下文切换的开销。而轻量级锁通过JAVA中CAS的实现方式,避免了这种上下文切换的开销。