自旋

自旋锁与互斥锁

百般思念 提交于 2019-12-18 18:10:11
自旋锁(spinlock)与互斥锁(mutex)是并发编程中两个重要的概念。它们的主要作用是:对共享资源加锁以阻止数据的并发访问,从而保证数据一致性。但是它们也有一些不同点。本文主要介绍这些不同点,并说明我们什么时候该用自旋锁,什么时候该用互斥锁。 理论基础 理论上,当一个线程尝试去获取一个互斥锁,但由于该互斥锁已经被其它线程获取而没有成功时,它会立刻进入休眠状态,从而让出CPU时间,允许其它线程运行。它将持续休眠直到最终被唤醒,唤醒的条件是之前获取到该互斥锁的线程释放了互斥锁; 对比一下,当一个线程尝试去获取一个自旋锁,但由于该自旋锁已经被其它线程获取而没有成功时, 它将会反复获取它(在英文中叫polling),直到最终成功。因此在获取自旋锁的时候,该线程不会让出CPU时间,其它线程将不能运行,当然,操作系统不会允许一个线程一直阻塞整个系统的运行,在某个线程花完了它的CPU运行总时间后,它会强制切换到另外线程执行。(注:线程一次使用的CPU总时间的最大上限可以通过ulimit -t查看,单位为秒) 问题 对于互斥锁,使线程进入休眠以及唤醒线程都是比较昂贵的操作,需要相当多的CPU指令,花费较长的CPU时间。如果A线程获取到该互斥锁后,只是持有了很短的一段时间就释放,那么B线程在获取互斥锁的过程中,B线程进入休眠以及被唤醒花费的CPU时间可能超过了B线程休眠的时间

多线程中的锁系统(四)-谈谈自旋锁

安稳与你 提交于 2019-12-18 08:49:05
阅读目录: 基础 自旋锁示例 SpinLock 继续SpinLock 总结 基础 内核锁:基于内核对象构造的锁机制,就是通常说的内核构造模式。 用户模式构造和内核模式构造 优点:cpu利用最大化。它发现资源被锁住,请求就排队等候。线程切换到别处干活,直到接受到可用信号,线程再切回来继续处理请求。 缺点:托管代码->用户模式代码->内核代码损耗、线程上下文切换损耗。 在锁的时间比较短时,系统频繁忙于休眠、切换,是个很大的性能损耗。 自旋锁:原子操作+自循环。通常说的用户构造模式。 线程不休眠,一直循环尝试对资源访问,直到可用。 优点:完美解决内核锁的缺点。 缺点:长时间一直循环会导致cpu的白白浪费,高并发竞争下、CPU的消耗特别严重。 混合锁:内核锁+自旋锁。 混合锁是先自旋锁一段时间或自旋多少次,再转成内核锁。 优点:内核锁和自旋锁的折中方案,利用前二者优点,避免出现极端情况(自旋时间过长,内核锁时间过短)。 缺点: 自旋多少时间、自旋多少次,这些策略很难把控。 在操作系统及net框架层,这块算法策略做的已经非常优了,有些API函数也提供了时间及次数可配置项,让使用者根据需求自行判断。 自旋锁示例 来看下我们自己简单实现的自旋锁:       int signal = 0; var li = new List<int>(); Parallel.For(0, 1000 *

Java 多线程之自旋锁

不羁的心 提交于 2019-12-17 15:17:08
一、什么是自旋锁? 自旋锁(spinlock):是指当一个线程在获取锁的时候,如果锁已经被其它线程获取,那么该线程将循环等待,然后不断的判断锁是否能够被成功获取,直到获取到锁才会退出循环。 获取锁的线程一直处于活跃状态,但是并没有执行任何有效的任务,使用这种锁会造成 busy-waiting 。 它是为实现保护共享资源而提出一种锁机制。其实,自旋锁与互斥锁比较类似,它们都是为了解决对某项资源的互斥使用。无论是互斥锁,还是自旋锁,在任何时刻,最多只能有一个保持者,也就说,在任何时刻最多只能有一个执行单元获得锁。但是两者在调度机制上略有不同。对于互斥锁,如果资源已经被占用,资源申请者只能进入睡眠状态。但是自旋锁不会引起调用者睡眠,如果自旋锁已经被别的执行单元保持,调用者就一直循环在那里看是否该自旋锁的保持者已经释放了锁,”自旋”一词就是因此而得名。 二、Java如何实现自旋锁? 下面是个简单的例子: public class SpinLock { private AtomicReference<Thread> cas = new AtomicReference<Thread>(); public void lock() { Thread current = Thread.currentThread(); // 利用CAS while (!cas.compareAndSet(null,

JVM中锁优化简介

两盒软妹~` 提交于 2019-12-16 09:41:27
【推荐】2019 Java 开发者跳槽指南.pdf(吐血整理) >>> 本文将简单介绍HotSpot虚拟机中用到的锁优化技术。 自旋锁 互斥同步对性能最大的影响是阻塞的实现,挂起线程和恢复线程的操作都需要转入内核态中完成,这些操作给系统的并发性能带来了很大的压力。而在很多应用上,共享数据的锁定状态只会持续很短的一段时间。若实体机上有多个处理器,能让两个以上的线程同时并行执行,我们就可以让后面请求锁的那个线程原地自旋(不放弃CPU时间),看看持有锁的线程是否很快就会释放锁。为了让线程等待,我们只须让线程执行一个忙循环(自旋),这项技术就是自旋锁。 如果锁长时间被占用,则浪费处理器资源,因此自旋等待的时间必须要有一定的限度,如果自旋超过了限定的次数仍然没有成功获得锁,就应当使用传统的方式去挂起线程了(默认10次)。 JDK1.6引入自适应的自旋锁:自旋时间不再固定,由前一次在同一个锁上的自旋时间及锁的拥有者的状态来决定。如果在同一个锁对象上,自旋等待刚刚成功获得过锁,并且持有锁的线程正在运行中,那么虚拟机就会认为这次自旋也很有可能再次成功,进而它将允许自旋等待持续相对更长的时间。 锁削除 锁削除是指虚拟机即时编译器在运行时,对一些代码上要求同步,但是被检测到不可能存在共享数据竞争的锁进行削除(主要判定依据来源于逃逸分析的数据支持,如果判断到一段代码中

JAVA 1.6锁状态转换

北战南征 提交于 2019-12-14 22:29:13
JVM 学不好 并发就学不好 面试问题 Object 有哪些方法 syn实现过程 wait notify 为什么要设计到Object上而不是接口?虽然可以 但是面向对象的思想 子类 object.wait thread.sleep 的区别? wait释放锁 挂起 32位锁状态() JDK1.6对锁优化 锁的状态从 无锁->偏向锁->轻量级锁->重量级锁(把所有等待线程放入) JDK1.6之前 synchon 性能不高 ,之后优化 建议使用synchon 分段锁 curruntlok 用的就是syn JVM源码 内核态到核心态 切换 耗费性能 轻量级锁 拿不到锁就阻塞式不合理的 所以用自适应的自旋锁 但是存在激烈的竞争 自旋次数多会浪费cpu,以短暂cpu负载 换取线程笨重的切换 多次自旋,浪费cpu 导致锁的膨胀 -> 重量级锁 多线程进入 能否抢到锁 抢不到进入队列 重量级锁 :Entry List 同步等待锁的队列 synchoronized是JVM的内置锁,而lock是Java代码实现的。lock是sync对的扩展,完全可以替代后者。lock可以重入,允许同一个线程连续多次获得同一把锁。其次,lock独有的功能有: 1、可以响应中断,sync要么获得锁执行,要么保持等待。而重入锁可以响应中断,使得线程在迟迟得不到锁的情况下,不用再等待。主要由lockInterruptibly

Java多线程进阶—— J.U.C之atomic框架:LongAdder

和自甴很熟 提交于 2019-12-13 17:24:27
一、LongAdder简介 JDK1.8时, java.util.concurrent.atomic 包中提供了一个新的原子类: LongAdder 。 根据Oracle官方文档的介绍,LongAdder在高并发的场景下会比它的前辈————AtomicLong 具有更好的性能,代价是消耗更多的内存空间: 那么,问题来了: 为什么要引入 LongAdder ? AtomicLong 在高并发的场景下有什么问题吗? 如果低并发环境下, LongAdder 和 AtomicLong 性能差不多,那 LongAdder 是否就可以替代 AtomicLong 了? 为什么要引入LongAdder? 我们知道, AtomicLong 是利用了底层的CAS操作来提供并发性的 ,比如 addAndGet 方法: 上述方法调用了 Unsafe 类的 getAndAddLong 方法,该方法是个 native 方法,它的逻辑是采用自旋的方式不断更新目标值,直到更新成功。 在并发量较低的环境下,线程冲突的概率比较小,自旋的次数不会很多。但是,高并发环境下,N个线程同时进行自旋操作,会出现大量失败并不断自旋的情况,此时 AtomicLong 的自旋会成为瓶颈 。 这就是 LongAdder 引入的初衷—— 解决高并发环境下 AtomicLong 的自旋瓶颈问题 。 LongAdder快在哪里? 既然说到

Java自旋锁

萝らか妹 提交于 2019-12-11 07:05:20
参考: https://blog.csdn.net/fuyuwei2015/article/details/83387536 自旋锁(spinlock):是指当一个线程在获取锁的时候,如果锁已经被其它线程获取,那么该线程将循环等待,然后不断的判断锁是否能够被成功获取,直到获取到锁才会退出循环。 获取锁的线程一直处于活跃状态,但是并没有执行任何有效的任务,使用这种锁会造成busy-waiting。 自旋锁的缺点 使用自旋锁会有以下一个问题: 1. 如果某个线程持有锁的时间过长,就会导致其它等待获取锁的线程进入循环等待,消耗CPU。使用不当会造成CPU使用率极高。 2. 上面Java实现的自旋锁不是公平的,即无法满足等待时间最长的线程优先获取锁。不公平的锁就会存在“线程饥饿”问题。 自旋锁的优点 自旋锁不会使线程状态发生切换,一直处于用户态,即线程一直都是active的;不会使线程进入阻塞状态,减少了不必要的上下文切换,执行速度快 非自旋锁在获取不到锁的时候会进入阻塞状态,从而进入内核态,当获取到锁的时候需要从内核态恢复,需要线程上下文切换。 (线程被阻塞后便进入内核(Linux)调度状态,这个会导致系统在用户态与内核态之间来回切换,严重影响锁的性能) 来源: CSDN 作者: 不带刺仙人球 链接: https://blog.csdn.net/zyzn1425077119

多线程锁

烈酒焚心 提交于 2019-12-10 02:18:07
1、互斥锁: Mutex属于sleep-waiting类型的锁 。例如在一个双核的机器上有两个线程(线程A和线程B),它们分别运行在Core0和Core1上。假设线程A想要通过pthread_mutex_lock操作去得到一个临界区的锁,而此时这个锁正被线程B所持有,那么线程A就会被阻塞,Core0会在此时进行上下文切换(Context Switch)将线程A置于等待队列中,此时Core0就可以运行其它的任务而不必进行忙等待。 2、 自旋锁(Spin lock) 自旋锁与互斥锁有点类似,只是自旋锁不会引起调用者睡眠,如果自旋锁已经被别的执行单元保持,调用者就一直循环在那里看是否该自旋锁的保持者已经释放了锁,“自旋锁”的作用 是为了解决某项资源的互斥使用。因为自旋锁不会引起调用者睡眠,所以自旋锁的效率远高于互斥锁。 自旋锁的不足之处 : 自旋锁一直占用着CPU,他在未获得锁的情况下,一直运行(自旋),所以占用着CPU,如果不能在很短的时间内获得锁,这无疑会使CPU效率降低。 在用自旋锁时有可能造成死锁,当递归调用时有可能造成死锁,调用有些其他函数也可能造成死锁,如 copy_to_user()、copy_from_user()、kmalloc()等。 因此我们要慎重使用自旋锁,自旋锁只有在内核可抢占式或SMP的情况下才真正需要,在单CPU且不可抢占式的内核下,自旋锁的操作为空操作。

spinlock与linux内核调度的关系

烈酒焚心 提交于 2019-12-09 20:22:18
作者: 刘洪涛, 华清远见嵌入式学院 高级讲师,ARM公司授权ATC讲师。 关于自旋锁用法介绍的文章,已经有很多,但有些细节的地方点的还不够透。我这里就把我个人认为大家容易有疑问的地方拿出来讨论一下。 一、自旋锁(spinlock)简介 自旋锁在同一时刻只能被最多一个内核任务持有,所以一个时刻只有一个线程允许存在于临界区中。这点可以应用在多处理机器、或运行在单处理器上的抢占式内核中需要的锁定服务。 二、信号量简介 这里也介绍下信号量的概念,因为它的用法和自旋锁有相似的地方。 Linux中的信号量是一种睡眠锁。如果有一个任务试图获得一个已被持有的信号量时,信号量会将其推入等待队列,然后让其睡眠。这时处理器获得自由去执行其它代码。当持有信号量的进程将信号量释放后,在等待队列中的一个任务将被唤醒,从而便可以获得这个信号量。 三、自旋锁和信号量对比 在很多地方自旋锁和信号量可以选择任何一个使用,但也有一些地方只能选择某一种。下面对比一些两者的用法。 表1-1自旋锁和信号量对比 应用场合 信号量or 自旋锁 低开销加锁(临界区执行时间较快) 优先选择 自旋锁 低开销加锁(临界区执行时间较长) 优先选择 信号量 临界区可能包含引起睡眠的代码 不能选自旋锁,可以选择 信号量 临界区位于非进程上下文时,此时不能睡眠 优先选择 自旋锁 ,即使选择信号量也只能用 down_trylock 非阻塞的方式

面试官:JVM锁优化都优化了啥?

家住魔仙堡 提交于 2019-12-06 21:09:51
从JDK1.6开始,JVM对锁进行了各种优化,目的就是为了在线程间更高效的共享数据和解决互斥同步的问题。从锁优化的话题开始,可以引申出很多考点面试题,比如锁优化的技术、各优化技术的细节、CAS实现原理、CAS的ABA问题及如何解决等,持续发散还会引发更多问题,例如逃逸分析等,可以看出技术点都是相关联的,需要不断积累和梳理。 面试官:JVM实现了哪些锁优化技术? 小白:自旋锁、自适应自旋锁、锁粗化、锁消除、偏向锁、轻量级锁。 面试官:介绍一下自旋锁?为什么引入自旋锁? 小白:自旋锁就是在请求获取锁,又不能马上获取到时,让当前线程在不放弃处理器执行时间的情况下执行忙循环,尝试等待锁被释放,再获取锁。引入自旋锁是为了节省线程挂起和恢复的开销。 面试官:你刚刚说引入自旋锁节省了线程挂起和恢复的开销,但循环也是需要占用处理器时间的,那这个自旋的次数如何控制? 小白:默认是10次,也可以通过JVM参数-XX:PreBlockSpin配置,当然这些自旋都是固定的,所以引入了自适应自旋锁,自旋的次数由前一次在同一个锁上的自旋次数和锁的拥有者的状态来决定。如果前面线程成功获取锁并且正常运行,那么本次获取锁的可能性很大,所以自旋的次数相对多一些;如果前面线程很少成功获取锁,那么本次获取锁的概率也很小,就可能不执行自旋了。 面试官:锁粗化优化了什么? 小白:如果在一段代码中同一线程反复获取