前言
自旋锁是个比较基础的知识点了,应该很熟悉的掌握。
一、什么是自旋锁?
自旋”可以理解为“自我旋转”,这里的“旋转”指“循环”,比如 while 循环或者 for 循环。“自旋”就是自己在这里不停地循环,直到目标达成。而不像普通的锁那样,如果获取不到锁就进入阻塞。
自旋锁并不会释放CPU的时间片,而是通过自旋的方式不断地去尝试获取锁,如果尝试获取失败,那么就一直尝试,直到成功获取到为止。
二、为什么要用自旋锁?
如果一个临界区的内容很“短小”,那么如果使用线程获取不到锁就“阻塞”的方式来加锁,那么线程状态切换的开销很可能大于临界区内容执行的开销,效率较差。如果一个线程在发现临界区已经被“占用”的情况下,通过一定次数的[自旋]来等待这个已经获取临界区使用权的线程释放锁,那么就可以一定程度下避免频繁的线程切换,从而提高效率。
三、实例讲解
1.5开始,java.util.concurrent.atomic包中的原子类基本都有自旋锁的影子。
AtomicInteger
public final int getAndIncrement() {
return unsafe.getAndAddInt(this, valueOffset, 1);
}
看下这个自增调用的方法
public final int getAndAddInt(Object var1, long var2, int var4) {
int var5;
do {
var5 = this.getIntVolatile(var1, var2);
} while(!this.compareAndSwapInt(var1, var2, var5, var5 + var4));
return var5;
}
这里的do while就是一个自旋操作,如果在修改过程中遇到其它线程争夺修改导致自己修改失败的情况下,就一直死循环重新去尝试加一,直到成功为止。
四、自己实现一个可重入的自旋锁
TODO : ==''
五、缺点及适应场景
缺点
避免线程状态切换带来的开销的同时,引入了新的开销,因为自旋锁需要一直不断地去尝试获取锁。如果锁一直不被释放,或者锁的耗时过长,都会导致自旋的无用尝试,白白浪费处理器资源。
适用场景
自旋锁的使用需要同时满足以下两个条件:
- 并发度不高
- 临界区简短不复杂
以上两个条件其实都是为了保证自旋可以尽快结束,避免浪费CPU资源。
总结
自旋锁可以提高程序效率,但是一定要注意使用场景,否则会适得其反。