Java并发编程——深入理解自旋锁
2021-03-22 19:28
标签:线程的状态 java 多次 java并发编程 strong 其他 多核 理解 lock 自旋锁(spinlock):是指当一个线程在获取锁的时候,如果锁已经被其它线程获取,那么该线程将循环等待,然后不断的判断锁是否能够被成功获取,直到获取到锁才会退出循环。 先看一个实现自旋锁的例子,java.util.concurrent包里提供了很多面向并发编程的类. 使用这些类在多核CPU的机器上会有比较好的性能.主要原因是这些类里面大多使用(失败-重试方式的)乐观锁而不是synchronized方式的悲观锁. lock()方法利用的CAS,当第一个线程A获取锁的时候,能够成功获取到,不会进入while循环,如果此时线程A没有释放锁,另一个线程B又来获取锁,此时由于不满足CAS,所以就会进入while循环,不断判断是否满足CAS,直到A线程调用unlock方法释放了该锁。 通过之前的AtomicReference类创建了一个自旋锁cas,然后创建两个线程,分别执行,结果如下: 通过对输出结果的分析我们可以得知,首先假定线程一在执行lock方法的时候获得了锁,通过方法 cas.compareAndSet(null, current) 将引用改为线程一的引用,跳过while循环,执行打印函数 而线程二此时也进入lock方法,在执行比较操作的时候发现,expect value != update value,于是进入while循环,打印 i am spinning。由以下红字可以得出结论,Java中的一个线程并不是总是占着cpu时间片不放,一直执行完的,而是采用抢占式调度,所以出现了上面两个线程交替执行的现象 Java线程的实现是通过映射到系统的轻量级线程上,轻量级线程有对应系统的内核线程,内核线程的调度由系统调度器来调度的,所以Java的线程调度方式取决于系统内核调度器,只不过刚好目前主流操作系统的线程实现都是抢占式的。 使用自旋锁会有以下一个问题: 文章开始的时候的那段代码,仔细分析一下就可以看出,它是不支持重入的,即当一个线程第一次已经获取到了该锁,在锁释放之前又一次重新获取该锁,第二次就不能成功获取到。由于不满足CAS,所以第二次获取会进入while循环等待,而如果是可重入锁,第二次也是应该能够成功获取到的。 例如将代码改成如下: 则运行结果将会无限打印,陷入无终止的循环!? 为了实现可重入锁,我们需要引入一个计数器,用来记录获取锁的线程数。 同样lock方法会先判断是否当前线程已经拿到了锁,拿到了就让count加一,可重入,然后直接返回!而unlock方法则会首先判断当前线程是否拿到了锁,如果拿到了,就会先判断计数器,不断减一,不断解锁! 本文到这里就结束了,感谢看到最后的朋友,都看到最后了,点个赞再走啊,如有不对之处还请多多指正。 Java并发编程——深入理解自旋锁 标签:线程的状态 java 多次 java并发编程 strong 其他 多核 理解 lock 原文地址:https://blog.51cto.com/14969174/2542996
获取锁的线程一直处于活跃状态,但是并没有执行任何有效的任务,使用这种锁会造成busy-waiting。2.Java如何实现自旋锁?
class spinlock {
private AtomicReference
自旋锁验证代码
package ddx.多线程;
import java.util.concurrent.atomic.AtomicReference;
public class 自旋锁 {
public static void main(String[] args) {
AtomicReference
0
I am spin
I am spin
I am spin
I am spin
I am spin
I am spin
I am spin
I am spin
I am spin
I am spin
I am spin
I am spin
I am spin
I am spin
I am spin
I am spin
1
I am spin
I am spin
I am spin
I am spin
I am spin
2
3
4
5
6
7
8
9
I am spin
0
1
2
3
4
5
6
7
8
9
3.自旋锁存在的问题
1. 如果某个线程持有锁的时间过长,就会导致其它等待获取锁的线程进入循环等待,消耗CPU。使用不当会造成CPU使用率极高。
2. 上面Java实现的自旋锁不是公平的,即无法满足等待时间最长的线程优先获取锁。不公平的锁就会存在“线程饥饿”问题。4.自旋锁的优点
5.可重入的自旋锁和不可重入的自旋锁
而且,即使第二次能够成功获取,那么当第一次释放锁的时候,第二次获取到的锁也会被释放,而这是不合理的。@Override
public void run() {
slock.lock(); //上锁
slock.lock(); //再次获取自己的锁!由于不可重入,则会陷入循环
for (int i = 0; i
public class ReentrantSpinLock {
private AtomicReference
?可重入自旋锁代码验证
//可重入自旋锁验证
class Task1 implements Runnable{
private AtomicReference
6.自旋锁与互斥锁异同点
7.总结
结尾