一、什么是自旋锁?
自旋锁与互斥锁类似,通过“自旋”使当前线程不放弃CPU时间片,而是忙等待,直到锁的持有者释放了要获取的锁,所以可见“自旋锁”是一种非阻塞锁。
在叙述一下,阻塞锁,当一个线程A在获取到锁时,再有线程B试图获取锁时,那么线程B将会挂起(阻塞);而自旋锁是,当线程B试图获取锁时,如果获取不到,则处于自旋,不释放CPU资源,减少线程上下文切换开销,以便当线程A释放锁,直接运行。
二、自旋锁可能引起的问题:
1.过多占据CPU时间:如果锁的当前持有者长时间不释放该锁,那么等待者将长时间的占据cpu时间片,导致CPU资源的浪费,因此可以设定一个时间,当锁持有者超过这个时间不释放锁时,等待者会放弃CPU时间片阻塞;
2.死锁问题:试想一下,有一个线程连续两次试图获得自旋锁(比如在递归程序中),第一次这个线程获得了该锁,当第二次试图加锁的时候,检测到锁已被占用(其实是被自己占用),那么这时,线程会一直等待自己释放该锁,而不能继续执行,这样就引起了死锁。因此递归程序使用自旋锁应该遵循以下原则:递归程序决不能在持有自旋锁时调用它自己,也决不能在递归调用时试图获得相同的自旋锁。
三、JAVA中一种自旋锁的实现
package com.test.lock;
// 锁接口
public interface Lock {
public void lock();
public void unlock();
}
package com.test.lock;
import java.util.concurrent.atomic.AtomicBoolean;
/**
* 测试-设置自旋锁,使用AtomicBoolean原子变量保存状态
* 每次都使用getAndSet原子操作来判断锁状态并尝试获取锁
* 缺点是getAndSet底层使用CAS来实现,一直在修改共享变量的值,会引发缓存一致性流量风暴
* **/
public class TASLock implements Lock{
private AtomicBoolean mutex = new AtomicBoolean(false);
@Override
public void lock() {
// getAndSet方法会设置mutex变量为true,并返回mutex之前的值
// 当mutex之前是false时才返回,表示获取锁
// getAndSet方法是原子操作,mutex原子变量的改动对所有线程可见
while(mutex.getAndSet(true)){
}
}
@Override
public void unlock() {
mutex.set(false);
}
public String toString(){
return "TASLock";
}
}