自旋锁
实现原理(while循环+CAS(compareAndSet))
- 一个锁已经被一个线程锁持有,其他尝试获取该锁的线程并不会立即阻塞,而是采用循环的方式不停地尝试去获取锁,直至获取成功,跳出循环
- 现实生活中的例子:公用洗衣机,一个人想去洗衣服,但是此时洗衣机正在被占用着,那么这个人有两种选择,要么一直在洗衣机前等待,等到该洗衣机洗完,另一种每隔一段时间再回来看看,洗衣机是否已经洗完
代码示例和解释
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;
/**
* 自旋锁:
* 实现原理(while循环+CAS(compareAndSet))
* 一个锁已经被一个线程锁持有,其他尝试获取该锁的线程并不会立即阻塞,而是采用循环的方式不停地尝试去获取锁,直至获取成功,跳出循环
* 现实生活中的例子:公用洗衣机,一个人想去洗衣服,但是此时洗衣机正在被占用着,那么这个人有两种选择,要么一直在洗衣机前等待,等到该洗衣机洗完,另一种每隔一段时间再回来看看,洗衣机是否已经洗完
*/
public class SpinLock {
public static void main(String[] args){
SpinLockResource spinLockResource = new SpinLockResource();
new Thread(()->{
//t1线程获得锁
spinLockResource.getLock();
//睡眠5秒,让t2线程尝试获取锁
try {
Thread.sleep(5000);
} catch (Exception e) {
e.printStackTrace();
}
//t1线程释放锁
spinLockResource.removeLock();
},"t1").start();
//当前main线程暂停一秒
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(()->{
//t2线程去获取锁
spinLockResource.getLock();
//睡眠1秒,t2线程获取完锁
try {
Thread.sleep(1000);
} catch (Exception e) {
e.printStackTrace();
}
//t2线程释放锁
spinLockResource.removeLock();
},"t2").start();
}
}
//资源类
class SpinLockResource{
//原子引用线程,使用原子类的CAS方法
AtomicReference<Thread> atomicThread = new AtomicReference<>();
//获取锁方法
public void getLock(){
Thread thread = Thread.currentThread();//获取当前线程
//atomicThread.compareAndSet(null, thread)
//当前线程去和主内存中线程比较,如果此时主内存中线程为空,返回true,将当前线程设置进主内存中
//初始atomicThread.compareAndSet(null, thread)为true,当前线程会设置进主内存,整个取反为false,不会进入while循环
while(!atomicThread.compareAndSet(null, thread)){
System.out.println(Thread.currentThread().getName()+"\t try to getLock...");
}
System.out.println(Thread.currentThread().getName()+"\t getLock......");
}
//释放锁资源的方法
public void removeLock(){
Thread thread = Thread.currentThread();//获取当前线程
atomicThread.compareAndSet(thread, null);//主内存中为当前线程,将主内存中线程置为空,相当于释放了锁资源,供其他线程写入。
System.out.println(Thread.currentThread().getName()+"\t removeLock......");
}
}
打印结果:
t1线程获取锁之后,t2线程会循环尝试去获取锁,而不是一直阻塞
直到t1线程释放锁,t2线程才去跳出循环,获取锁
但是如果出现极端情况下,第一个线程一直没有释放锁,第二个线程不停地去循环,造成死循环,就会造成很大的cpu开销