什么是自旋锁?Java实现自旋锁原理详解

什么是自旋锁?Java实现自旋锁原理详解-mikechen

Java面试经常问到自旋锁相关的问题,什么是自旋锁?Java如何实现自旋锁?自旋锁的底层原理是怎样的?下面一一详解@mikechen

什么是自旋锁?

自旋锁(spinlock):是指当一个线程在获取锁的时候,如果锁已经被其它线程获取,那么该线程将循环等待,然后不断的判断锁是否能够被成功获取,直到获取到锁才会退出循环。

这种采用循环加锁,等待锁释放的机制就称为自旋锁(spinlock)。

 

自旋锁的优缺点

自旋锁的优点:

性能较高:自旋锁不会使线程状态切换,始终处于用户态,即线程始终处于活动状态,不会让线程进入阻塞状态,减少不必要的上下文切换,性能较高。

自旋锁避免了进程上下文的调度开销,因此对于线程只会阻塞很短时间的场合是有效的,所以相比互斥锁来说,会快一些开销。

 

自旋锁的缺点

1)死锁:试图递归地获得自旋锁必然会引起死锁:递归程序的持有实例在第二个实例循环,以试图获得相同自旋锁时,不会释放此自旋锁。

2)在等待锁时进入循环会占用CPU,若等待的线程很多,对CPU的消耗会比较大。

2)不适合需要长时间等待的任务或线程,不适合大量线程等待的场景。

 

自旋锁的使用场景

1)等待时间比较短的任务中;

2)线程数量不太多的应用中;

3)当等待时间长或线程数量很大时,可以使用其他锁(比如:可重入锁)。

 

Java如何实现自旋锁?

看一个Java实现的例子:

public class SpinLock {

private AtomicReference<Thread> cas = new AtomicReference<Thread>();

public void lock() {

Thread current = Thread.currentThread();

// 利用CAS

while (!cas.compareAndSet(null, current)) {

// DO nothing

}

}

public void unlock() {

Thread current = Thread.currentThread();

cas.compareAndSet(current, null);

}

}

lock获取锁的方法利用的CAS,当第一个线程A获取锁的时候,能够成功获取到,不会进入while循环,如果此时线程A没有释放锁,另一个线程B又来获取锁,此时由于不满足CAS,所以就会进入while循环,不断判断是否满足CAS,直到A线程调用unlock方法释放了该锁。

Java并发包中的很多类都使用了CAS技术,是实现我们平时所说的自旋锁或乐观锁的核心操作。

 

CAS的算法

它的实现很简单,就是用一个预期的值和内存值进行比较,如果两个值相等,就用预期的值替换内存值,并返回 true。否则,返回 false。

CAS机制当中使用了3个基本操作数:内存地址V,旧的预期值A,要修改的新值B

更新一个变量的时候,只有当变量的预期值A和内存地址V当中的实际值相同时,才会将内存地址V对应的值修改为B

CAS的实现原理

什么是自旋锁?Java实现自旋锁原理详解-mikechen

如上图显示,两个线程:线程1和线程2同时修改值,如果通过CAS来实现,具体流程如下:

  1. 在内存地址V当中,存储着值为7的变量
  2. 线程1想要把变量的值增加1,对线程1来说,旧的预期值A=7,要修改的新值B=8
  3. 线程2抢先一步,把内存值V修改:8
  4. 线程1开始提交更新,首先对比了预期值A=7和实际值V的比较8(Compare),发现A不等于V的实际值,提交失败

关于CAS,可以查看《CAS实现原理详解》这篇文章。

 

陈睿mikechen

10年+大厂架构经验,资深技术专家,就职于阿里巴巴、淘宝、百度等一线互联网大厂。

关注「mikechen」公众号,获取更多技术干货!

后台回复面试即可获取《史上最全阿里Java面试题总结》,后台回复架构,即可获取《阿里架构师进阶专题全部合集

评论交流
    说说你的看法