Java并发编程中自旋锁会浪费 CPU 资源并导致错误
精选
原创
©著作权归作者所有:来自51CTO博客作者小二上酒8的原创作品,请联系作者获取转载授权,否则将追究法律责任
在并发编程中,自旋锁(spin locks )想必大家都不陌生。
自旋锁一个非常经典的使用场景是CAS(即比较和交换),是一种无锁的思想(说白了就是使用了无限循环),用来解决更新数据的问题高并发场景。
atomic包下的很多类,如AtomicInteger、AtomicLong、AtomicBoolean等,都是用CAS实现的。
我们以AtomicInteger类为例,它incrementAndGet不会每次都给变量加 1。
<b>public</b> <b>final</b> <b>int</b> incrementAndGet() {
<b>return</b> unsafe.getAndAddInt(<b>this</b>, valueOffset, 1) + 1;
}
它的底层是用自旋锁实现的:
<b>public</b> <b>final</b> <b>int</b> getAndAddInt(Object <b>var</b>1, <b>long</b> <b>var</b>2, <b>int</b> <b>var</b>4) {
<b>int</b> <b>var</b>5;
<b>do</b> {
<b>var</b>5 = <b>this</b>.getIntVolatile(<b>var</b>1, <b>var</b>2);
} <b>while</b>(!<b>this</b>.compareAndSwapInt(<b>var</b>1, <b>var</b>2, <b>var</b>5, <b>var</b>5 + <b>var</b>4));
<b>return</b> <b>var</b>5;
}
在do…while无限循环中,不断地进行数据的比较和交换。如果一直失败,就会一直重试。
在高并发的情况下,compareAndSwapInt大概率会失败,从而导致CPU不断自旋,严重浪费CPU资源。
那么,如果这个问题解决了呢?
解决方案是使用LockSupport类的方法parkNanos。
<b>private</b> <b>boolean</b> compareAndSwapInt2(Object <b>var</b>1, <b>long</b> <b>var</b>2, <b>int</b> <b>var</b>4, <b>int</b> <b>var</b>5) {
<b>if</b>(<b>this</b>.compareAndSwapInt(<b>var</b>1,<b>var</b>2,<b>var</b>4, <b>var</b>5)) {
<b>return</b> <b>true</b>;
} <b>else</b> {
LockSupport.parkNanos(10);
<b>return</b> false;
}
}
当CAS失败时,调用LockSupport类的parkNanos方法进行睡眠,这相当于调用Thread.Sleep方法。
这样可以有效减少因频繁旋转而造成的CPU资源过度浪费的问题。