自旋锁
多线程对性能最大的影响是阻塞,挂起线程和恢复线程的操作都需要转入内核态中完成,这个过程时需要消耗系统资源的。实际上,许多应用的共享数据的锁定状态只会持续很短的时间,为了这段时间去挂起和恢复线程并不值得。于是,我们可以让后面请求锁的线程“稍等一会”,等待持有锁的线程会不会很快就释放了锁。为了让线程在原地等待,我们只须让线程执行一个忙循环,相当于让这个线程不停地在原地旋转,这就是自旋锁。
需要注意的一点,自旋锁的“锁”是赋予的一个名字,实际上,自旋锁本身并无锁,我们只是让它去在原地旋转(并没有挂起当前线程)来实现。
JDK对自旋锁的支持
自旋锁在JDK1.4就开始引入,但是默认是关闭的,我们可以使用-XX:+UseSpinning参数来开启,在JDK1.6中就改为默认开启了。自旋等待不能代替阻塞,自旋本身虽然避免了线程切换的开销,但它是要占用处理器时间的, 所以如果锁被占用的时间很短,自旋的效果体现就不错,反之如果锁被占用的时间很长,那么自旋的线程就是在白白消耗处理器资源,反而会带来性能的浪费。
因此自旋等待的时间必须要有一定的限制,如果自旋超过了限定次数仍然没有获得锁,就应当去挂起线程了。自旋次数的默认值是10次,用户可以使用参数-XX:PreBlockSpin来更改。
在JDK1.6引入了自适应的自旋锁。自适应就是说自旋时间不再固定,而是由前一次在等待同一个锁时的自旋时间及锁的拥有者的状态来决定。如果在同一个锁对象上,自旋等待刚刚成功获得过锁,并且持有锁的线程正在运行中,那么就会认为这次自旋也很有可能再次成功,进而将允许自旋的时间设置长一些, 比如100次自旋(100个循环)。如果对于某个锁,自旋很少成功获得过,那在以后要获取这个锁时将可能省略掉自旋过程,以避免浪费系统资源。
有了自适应自旋锁,随着程序运行和性能监控信息的不断完善,jvm对程序中锁的状况预测就会越来越准确,效果也就越来越好。