什么是自旋锁

多线程中,对共享资源进行访问,为了防止并发引起的相关问题,通常都是引入锁的机制来处理并发问题。

获取到资源的线程A对这个资源加锁,其他线程比如B要访问这个资源首先要获得锁,而此时A持有这个资源的锁,只有等待线程A逻辑执行完,释放锁,这个时候B才能获取到资源的锁进而获取到该资源。

这个过程中,A一直持有着资源的锁,那么没有获取到锁的其他线程比如B怎么办?通常就会有两种方式:

1. 一种是没有获得锁的进程就直接进入阻塞(BLOCKING),这种就是互斥锁

2. 另外一种就是没有获得锁的进程,不进入阻塞,而是一直循环着,看是否能够等到A释放了资源的锁。

上述的两种方式,学术上,就有几种不同的定义方式,大学的时候 学习的是C++, 《C++ 11》中就有这样的描述:

自旋锁(spin lock)是一种非阻塞锁,也就是说,如果某线程需要获取锁,但该锁已经被其他线程占用时,该线程不会被挂起,而是在不断的消耗CPU的时间,不停的试图获取锁。

互斥量(mutex)是阻塞锁,当某线程无法获取锁时,该线程会被直接挂起,该线程不再消耗CPU时间,当其他线程释放锁后,操作系统会激活那个被挂起的线程,让其投入运行。

而《linux内核设计与实现》经常提到两种态,一种是内核态,一种是用户态,对于自旋锁来说,自旋锁使线程处于用户态,而互斥锁需要重新分配,进入到内核态。这里大家对内核态和用户态有个初步的认知就行了,用户态比较轻,内核态比较重。用户态和内核态这个也是linux中必备的知识基础,借鉴这个,可以进行很多程序设计语言API上的优化,就比如说javaio的部分,操作io的时候,先是要从用户态,进入内核态,再用内核态去操作输入输出设备的抽象,这里减少用户态到内核态的转换就是新io的一部分优化,后面再聊。

wiki中的定义如下:

自旋锁是计算机科学用于多线程同步的一种锁,线程反复检查锁变量是否可用。由于线程在这一过程中保持执行,因此是一种忙等待。

自旋锁避免了进程上下文的调度开销,因此对于线程只会阻塞很短时间的场合是有效的。因此操作系统的实现在很多地方往往用自旋锁。Windows操作系统提供的轻型读写锁(SRW Lock)内部就用了自旋锁。显然,单核CPU不适于使用自旋锁,这里的单核CPU指的是单核单线程的CPU,因为,在同一时间只有一个线程是处在运行状态,假设运行线程A发现无法获取锁,只能等待解锁,但因为A自身不挂起,所以那个持有锁的线程B没有办法进入运行状态,只能等到操作系统分给A的时间片用完,才能有机会被调度。这种情况下使用自旋锁的代价很高。(红字部分是我给wiki编辑的词条,单核CPU不适合自旋锁,这个也只是针对单核单线程的情况,现在的技术基本单核都是支持多线程的)

为什么要使用自旋锁

互斥锁有一个缺点,他的执行流程是这样的 托管代码  - 用户态代码 - 内核态代码、上下文切换开销与损耗,假如获取到资源锁的线程A立马处理完逻辑释放掉资源锁,如果是采取互斥的方式,那么线程B从没有获取锁到获取锁这个过程中,就要用户态和内核态调度、上下文切换的开销和损耗。所以就有了自旋锁的模式,让线程B就在用户态循环等着,减少消耗。

自旋锁比较适用于锁使用者保持锁时间比较短的情况,这种情况下自旋锁的效率要远高于互斥锁。

自旋锁可能潜在的问题

  • 过多占用CPU的资源,如果锁持有者线程A一直长时间的持有锁处理自己的逻辑,那么这个线程B就会一直循环等待过度占用cpu资源
  • 递归使用可能会造成死锁,不过这种场景一般写不出来

CAS

就不写术语定义了,简单的理解就是这个CAS是由操作系统定义的,由若干指令组成的,这个操作具有原子性,这些指令如果执行,就会全部执行完,不会被中断。CAS有3个操作数,内存值V,旧的预期值A,要修改的新值B。当且仅当预期值A和内存值V相同时,将内存值V修改为B,否则什么都不做。

CAS的问题

  • 经典的CAS的ABA问题,上面提到了CAS操作的时候,要检测值有没有变化,如果一个值原来是A,后来变成了B, 后来又变成了A,CAS会认为没有发生变化。

解决方案:

1. 加版本号   1A - 2B -  3A

2. 对java而言,jdk1.5提供了AtomicStampedReference来解决这个问题

  • 只能保证一个共享变量的原子操作

CAS通常是对一个变量来进行原子操作的,所以如果对多个变量进行原子操作就会有问题了。

解决方案

1. 简单粗暴,加锁,反而加入了复杂性,最low的方式

2. 跟上面的加版本号的道理一样,就是将多个变量拼成一个变量(可以拼成一个字符串)

3. 对java而言,jdk1.5 提供了AtomicStampedReference,这个reference 就是个对象引用,把多个变量放在这个对象里即可

JAVA CAS封装

sun.misc.Unsafe是JDK里面的一个内部类,这个类当中有三个CAS的操作

JAVA自旋锁应用

Jdk1.5以后,提供了java.util.concurrent.atomic包,这个包里面提供了一组原子类。基本上就是当前获取锁的线程,执行更新的方法,其他线程自旋等待,比如atomicInteger类中的getAndAdd方法内部实际上使用的就是Unsafe的方法。

    /*** Atomically adds the given value to the current value.** @param delta the value to add* @return the previous value*/public final int getAndAdd(int delta) {return unsafe.getAndAddInt(this, valueOffset, delta);}

当然java中的syncronized关键字,在1.5中有了很大的优化,加入了偏隙锁也有人叫偏向锁,主要的实现方式就是在对象头markword中打上线程的信息,这样资源上的锁的获取就偏向了这个线程,后面,会涉及一系列的锁升级的问题,间隙锁 - 轻量锁 - 重量级锁 ,锁升级后面单独抽出来写一篇,这个轻量锁实际上就是使用的也是自旋锁的实现方式。

自旋锁以及Java中的自旋锁的实现相关推荐

  1. 浅谈Java中的各种锁

    在读很多并发文章中,会提及各种各样锁如公平锁,乐观锁等等,这篇文章介绍各种锁的分类.介绍的内容如下: 公平锁/非公平锁 可重入锁 独享锁/共享锁 互斥锁/读写锁 乐观锁/悲观锁 分段锁 偏向锁/轻量级 ...

  2. Java中的各种锁事

    本文来聊下Java中的各种锁 文章目录 锁概述 各种锁描述 本文小结 锁概述 本文来聊下Java中的各种锁,彻底理解Java中的各种锁. Java中的各种锁 序号 锁名称 应用 1 乐观锁 CAS 2 ...

  3. Java 中15种锁的介绍:公平锁,可重入锁,独享锁,互斥锁,乐观锁,分段锁,自旋锁等等...

    http://blog.51cto.com/13919357/2339446 Java 中15种锁的介绍 在读很多并发文章中,会提及各种各样锁如公平锁,乐观锁等等,这篇文章介绍各种锁的分类.介绍的内容 ...

  4. Java 中15种锁的介绍:公平锁,可重入锁,独享锁,互斥锁,乐观锁,分段锁,自旋锁等等

    Java 中15种锁的介绍 在读很多并发文章中,会提及各种各样锁如公平锁,乐观锁等等,这篇文章介绍各种锁的分类.介绍的内容如下: 公平锁 / 非公平锁 可重入锁 / 不可重入锁 独享锁 / 共享锁 互 ...

  5. java 自旋锁_搞懂Java中的自旋锁

    轻松搞懂Java中的自旋锁 前言 在之前的文章<一文彻底搞懂面试中常问的各种"锁">中介绍了Java中的各种"锁",可能对于不是很了解这些概念的同学 ...

  6. Java中的各种锁和数据库中的锁

    Java中的锁 公平和非公平锁:公平锁是指多个线程按照申请锁的顺序来获取锁非公平锁即打破这个顺序,后来的线程也可以提前获取锁. 在ReentrantLock中可以通过改变构造方法参数,变化锁.但是在s ...

  7. Java基础-Java中常用的锁机制与使用

    Java基础-Java中常用的锁机制与使用 锁lock或互斥mutex是一种同步机制,主要用于在存在多线程的环境中强制对资源进行访问限制.锁的主要作用为强制实施互斥排他以及并发控制策略.锁一般需要硬件 ...

  8. JUC学习:java中的各种锁详细介绍

    转自:https://www.cnblogs.com/jyroy/p/11365935.html ,我在网上看到了一篇很好的blog来介绍锁的相关内容,在这记录下来以供后面自己学习 Java提供了种类 ...

  9. Java 中的各种锁及其原理

    文章目录 概览 Synchronized锁 Synchronized 锁的底层类别 不同锁下对象头中的内容 偏向锁 轻量级锁 轻量级锁加锁过程 字节码层面 synchronized关键字最主要的三种使 ...

最新文章

  1. windows下安装android版reactnative
  2. [P1363] 幻想迷宫
  3. python输出价目表-Python:使用基于事件驱动的SAX解析XML
  4. 【线性规划与网络流24题】孤岛营救问题 分层图
  5. Jenkins 2.322 安装 自定义插件
  6. 计算机图形学图形旋转_计算机图形学翻译
  7. stm32经典笔试题_嵌入式面试经典30问
  8. redist mysql_redist命令操作(三)--集合Set
  9. Win11内存占用高怎么办,Win11内存占用高解决方法
  10. sourceTree 的使用
  11. 山东春季高考计算机知识点,山东春季高考专业知识点总结
  12. Hive集合数据类型(STRUCK,MAP,ARRAY)
  13. 【51单片机】(手把手教你)1602液晶屏-基础篇
  14. html显示ping值,jquery JS实现ping的功能(JS ping url)
  15. idea html设置字体大小,intellij idea设置(字体大小、背景)
  16. Python练习:四叶玫瑰数求解
  17. 2021黑马web前端
  18. 微信小程序 js中遍历list
  19. SAP ABAP EXCEL 下载模板并导入(数据批导)
  20. java 获取一天零点零分零秒时间戳

热门文章

  1. RabbitMQ 安装使用,Centos系统安装RabbitMQ、Docker安装启动RabbitMQ
  2. Flink on Yarn的两种模式及HA
  3. hcip难不难?华为认证考试难不难?
  4. 我的世界java无法连接服务器_java - 无法连接到Minecraft服务器 - SO中文参考 - www.soinside.com...
  5. 扫码登录的原理和实现
  6. 超微服务器硬盘红灯_服务器硬盘亮红灯崩溃怎么办?数据丢失都是怎么找回的...
  7. 1028: 安全路径(2014年中南大学研究生复试机试题 )
  8. RF在智能座舱测试中的应用
  9. vue循环jq渲染网页页面
  10. 乔布斯的简历120万被拍卖,HR看了想打人……