Java中的自旋锁解析

 更新时间:2023年10月13日 09:21:31   作者:李长渊哦  
这篇文章主要介绍了Java中的自旋锁解析,自旋锁是指当一个线程尝试获取某个锁时,如果该锁已被其他线程占用,就一直循环检测锁是否被释放,而不是进入线程挂起或睡眠状态,需要的朋友可以参考下

一、自旋锁介绍

什么是自旋锁

自旋锁是指当一个线程尝试获取某个锁时,如果该锁已被其他线程占用,就一直循环检测锁是否被释放,而不是进入线程挂起或睡眠状态。

为什么要使用自旋锁

多个线程对同一个变量一直使用CAS操作,那么会有大量修改操作,从而产生大量的缓存一致性流量,因为每一次CAS操作都会发出广播通知其他处理器,从而影响程序的性能。

线程自旋与线程阻塞

阻塞的缺点显而易见,线程一旦进入阻塞(Block),再被唤醒的代价比较高,性能较差。自旋的优点是线程还是Runnable的,只是在执行空代码。当然一直自旋也会白白消耗计算资源,所以常见的做法是先自旋一段时间,还没拿到锁就进入阻塞。JVM在处理synchrized实现时就是采用了这种折中的方案,并提供了调节自旋的参数。

首先来对比一下互斥锁和自旋锁。

  • 互斥锁:从等待到解锁过程,线程会从block状态变为running状态,过程中有线程上下文的切换,抢占CPU等开销。
  • 自旋锁:从等待到解锁过程,线程一直处于running状态,没有上下文的切换。

虽然自旋锁效率比互斥锁高,但它会存在下面两个问题

  1. 自旋锁一直占用CPU,在未获得锁的情况下,一直运行,如果不能在很短的时间内获得锁,会导致CPU效率降低。
  2. 试图递归地获得自旋锁会引起死锁。递归程序决不能在持有自旋锁时调用它自己,也决不能在递归调用时试图获得相同的自旋锁。

由此可见,我们要慎重的使用自旋锁,自旋锁适合于锁使用者保持锁时间比较短并且锁竞争不激烈的情况。正是由于自旋锁使用者一般保持锁时间非常短,因此选择自旋而不是睡眠是非常必要的,自旋锁的效率远高于互斥锁。

二、代码举例

/**
 * @author lichangyuan
 * @create 2021-10-08 10:50
 */
public class SpinLock {
    public static void main(String[] args) throws InterruptedException {
        //设置100容量线程池
        ExecutorService executorService = Executors.newFixedThreadPool(100);
        //计数器用于阻塞
        CountDownLatch countDownLatch = new CountDownLatch(10);
        //创建自旋锁对象
        SimpleSpinningLock simpleSpinningLock = new SimpleSpinningLock();
        for (int i = 0; i < 10; i++) {
            executorService.execute(new Runnable() {
                @Override
                public void run() {
                    simpleSpinningLock.lock();
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println("子线程:" + Thread.currentThread().getName() + "执行");
                    simpleSpinningLock.unLock();
                    //确认已经连接完毕后再进行操作,将count值减1
                    countDownLatch.countDown();
                }
            });
        }
        //调用await()方法的线程会被挂起,它会等待直到count值为0才继续执行,没有则调用countDown则继续阻塞
        countDownLatch.await();
    }
}
class SimpleSpinningLock {
    /**
     * 持有锁的线程,null表示锁未被线程持有
     */
    private AtomicReference<Thread> sign = new AtomicReference<>();
    /**
     * 调用lock方法时,如果sign当前值为null,说明自旋锁还没有被占用,将sign设置为currentThread,并进行锁定。
     * 调用lock方法时,如果sign当前值不为null,说明自旋锁已经被其他线程占用,当前线程就会在while中继续循环检测。
     */
    public void lock() {
        //返回的正是执行当前代码指令的线程引用
        Thread currentThread = Thread.currentThread();
        //expect:它指定原子对象应为的值。
        //val:如果原子整数等于期望值,则该值指定要更新的值。
        while (!sign.compareAndSet(null, currentThread)) {
            //当ref为null的时候compareAndSet返回true,反之为false
            //通过循环不断的自旋判断锁是否被其他线程持有
        }
    }
    /**
     * 调用unlock方法时,会将sign置为空,相当于释放自旋锁。
     */
    public void unLock() {
        Thread currentThread = Thread.currentThread();
        //expect:它指定原子对象应为的值。
        //val:如果原子整数等于期望值,则该值指定要更新的值。
        sign.compareAndSet(currentThread, null);
    }
}

总结

由于自旋锁只是在当前线程不停地执行循环体,不进行线程状态的切换,因此响应速度更快。

但当线程数不停增加时,性能下降明显,因为每个线程都需要占用CPU时间。

如果线程竞争不激烈,并且保持锁的时间很短,则适合使用自旋锁。

到此这篇关于Java中的自旋锁解析的文章就介绍到这了,更多相关Java自旋锁内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • 你知道Spring如何解决所有循环依赖的吗

    你知道Spring如何解决所有循环依赖的吗

    这篇文章主要来和大家聊聊Spring 能解决所有循环依赖吗,文中的示例代码讲解详细,对我们学习Spring有一定的帮助,需要的小伙伴可以参考一下
    2023-07-07
  • java 设计模式之适配器模式的详解

    java 设计模式之适配器模式的详解

    这篇文章主要介绍了java 设计模式之适配器模式的详解的相关资料,需要的朋友可以参考下
    2017-07-07
  • Springboot中@Value的使用详解

    Springboot中@Value的使用详解

    这篇文章主要介绍了Springboot中@Value的使用详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2019-12-12
  • JAVA使用Ldap操作AD域的方法示例

    JAVA使用Ldap操作AD域的方法示例

    这篇文章主要介绍了JAVA使用Ldap操作AD域的方法示例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2019-04-04
  • java实现将域名解析成ip示例

    java实现将域名解析成ip示例

    这篇文章主要介绍了java实现将域名解析成ip示例,需要的朋友可以参考下
    2014-04-04
  • Java编程中使用throw关键字抛出异常的用法简介

    Java编程中使用throw关键字抛出异常的用法简介

    这篇文章主要介绍了Java编程中使用throw关键字抛出异常的用法,是Java入门学习中的基础知识,需要的朋友可以参考下
    2015-11-11
  • Mybatis saveAndUpdate空值不更新问题及解决

    Mybatis saveAndUpdate空值不更新问题及解决

    这篇文章主要介绍了Mybatis saveAndUpdate空值不更新问题及解决方案,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2023-02-02
  • 详解 Java 中 equals 和 == 的区别

    详解 Java 中 equals 和 == 的区别

    这篇文章主要介绍了详解 Java 中 equals 和 == 的区别的相关资料,equals 和 == 都是用来检测两个字符串是否相等,返回值也都是布尔型,但是两者在内部比较的处理中却不尽相同需要的朋友可以参考下
    2017-03-03
  • Java日常练习题,每天进步一点点(55)

    Java日常练习题,每天进步一点点(55)

    下面小编就为大家带来一篇Java基础的几道练习题(分享)。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧,希望可以帮到你
    2021-08-08
  • Java调用商品详情API的项目实践

    Java调用商品详情API的项目实践

    在现代电子商务网站中,商品详情API是一个重要的组件,本文就来介绍一下Java调用商品详情API的项目实践,具有一定的参考价值,感兴趣的可以了解一下
    2023-11-11

最新评论