自旋

【笔记】并发

拜拜、爱过 提交于 2020-02-26 07:39:48
一篇文章带你解析,乐观锁与悲观锁的优缺点 乐观锁与悲观锁 乐观锁与悲观锁不是指具体的什么类型的锁,而是指看待并发同步的角度。 定义 适用场景 使用 优缺点 悲观锁 悲观的认为,不加锁的并发操作一定会出问题。 写操作非常多 就是利用各种锁(synchronized独占锁) 保证数据安全 使用synchronized同步锁进行线程阻塞和唤醒切换以及用户态内核态间的切换操作额外浪费消耗cpu资源 乐观锁 认为对于同一个数据的并发操作,是不会发生修改的。 读操作非常多 无锁编程,采用的是CAS算法,典型的例子就是原子类,通过CAS自旋实现原子操作的更新。 不加锁会带来大量的性能提升。 冲突多时,自旋消耗大 在多线程竞争下,加锁、释放锁会导致比较多的上下文切换和调度延时,引起性能问题。 (2)一个线程持有锁会导致其它所有需要此锁的线程挂起。 (3)如果一个优先级高的线程等待一个优先级低的线程释放锁会导致优先级倒置,引起性能风险。 CAS 定义 CAS,compare and swap的缩写,比较并交换。 CAS 操作包含三个操作数 —— 内存位置(V)、预期原值(A)和新值(B)。有效地说明了“我认为位置 V 应该包含值 A;如果包含该值,则将 B 放到这个位置;否则,不要更改该位置,只告诉我这个位置现在的值即可。 存在的问题 ABA问题。 但是如果一个值原来是A,变成了B,又变成了A

锁的升级打怪:通俗易懂讲解偏向锁、轻量级锁和重量级锁

青春壹個敷衍的年華 提交于 2020-02-25 14:43:57
PART0: PART1: 首先通过一个小例子来解释一下三种锁的区别: 假如家里只有一个碗,当我自己在家时,没有人会和我争碗,这时即为偏向锁状态 当我和女朋友都在家吃饭时,如果女朋友不是很饿,则她会等我吃完再用我的碗去吃饭,这就是轻量级锁状态 当我和女朋友都很饿的时候,这时候就会去争抢这唯一的一个碗(贫穷的我)吃饭,这就是重量级锁状态 PART2: 我是一个线程,生活在JVM(Java虚拟机)中, 这一段日子过得有些无聊,整个世界似乎只有这一个人,天天忙着执行代码,想休息一下都很难。 我听说人类写的代码中有些特殊的地方,叫做 临界区 ,比如synchronized修饰的方法或者代码块,他们非常神奇,在同一时刻JVM老大只允许一个线程进入执行。 实际上,老大设置了一把锁,抢到了这把锁就可以执行,否则只能阻塞,等待别人释放锁。 老大说,阻塞就是不用干活了,老老实实地等着就行。 竟然还有这等美事! 赶紧让我阻塞一次吧。 可是老大又说:“每次设置锁我都得和操作系统打交道,请他在内核中维护一个什么Mutex(互斥量)的东西,他还得把你们这些线程阻塞,切换,这可是一笔巨大的费用啊,所以这些锁还是少用为妙。” 我运气也不好,我不知道执行了多少代码,调用了多少函数,竟然一次也没遇到临界区! 我想也许这个程序员编程时不小心,没有考虑多线程并发的情况; 也有可能是这些程序大部分都是无状态的

【整理】pinlock与mutex

那年仲夏 提交于 2020-02-24 16:50:30
内核同步措施:为了避免并发防止竞争,内核提供了一组同步方法来提供对共享数据的保护。 Linux同步机制从2.0到2.6以来不断发展完善。从最初的原子操作,到后来的信号量,从大内核锁到自旋锁。这些同步机制的发展伴随 Linux 从单处理器到对称多处理器 的过度,伴随着从非抢占内核到抢占内核的过度。锁机制越来越有效,也越来越复杂。 目前,内核中原子操作多用来做计数使用,其它情况最常用的是两种锁及它们的变种: 一个是自旋锁,另一个是信号量。 自旋锁 :专为防止多处理器并发而引入的一种锁,它在内核中大量应用于中断处理等部分(对于单处理器来说,防止中断处理中的并发可简单采用关闭中断的方式,不需要自旋锁)。 自旋锁最多只能被一个内核任务持有 ,如果一个内核任务试图请求一个已被争用(已经被持有)的自旋锁,那么这个任务就会一直进行忙循环——旋转——等待锁重新可用。要是锁未被争用,请求它的内核任务便能立刻得到它并且继续进行。自旋锁可以在任何时刻防止多于一个的内核任务同时进入临界区,因此这种锁可有效地避免多处理器上并发运行的内核任务竞争共享资源。 事实上,自旋锁的初衷就是:在短期间内进行轻量级的锁定。一个被争用的自旋锁使得请求它的线程在等待锁重新可用的期间进行自旋(特别浪费处理器时间),所以 自旋锁不应该被持有时间过长 。如果需要长时间锁定的话, 最好使用信号量。 自旋锁的基本形式如下: spin

信号量与自旋锁

懵懂的女人 提交于 2020-02-24 16:47:56
内核同步措施 为了避免并发,防止竞争。内核提供了一组同步方法来提供对共享数据的保护。 我们的重点不是介绍这些方法的详细用法,而是强调为什么使用这些方法和它们之间的差别。 Linux 使用的同步机制可以说从2.0到2.6以来不断发展完善。从最初的原子操作,到后来的信号量,从大内核锁到今天的自旋锁。这些同步机制的发展伴随 Linux从单处理器到对称多处理器的过度;伴随着从非抢占内核到抢占内核的过度。锁机制越来越有效,也越来越复杂。 目前来说内核中原子操作多用来做计数使用,其它情况最常用的是两种锁以及它们的变种:一个是自旋锁,另一个是信号量。我们下面就来着重介绍一下这两种锁机制。 自旋锁 ------------------------------------------------------ 自旋锁是专为防止多处理器并发而引入的一种锁,它在内核中大量应用于中断处理等部分(对于单处理器来说,防止中断处理中的并发可简单采用关闭中断的方式,不需要自旋锁)。 自旋锁最多只能被一个内核任务持有,如果一个内核任务试图请求一个已被争用(已经被持有)的自旋锁,那么这个任务就会一直进行忙循环——旋转——等待锁重新可用。要是锁未被争用,请求它的内核任务便能立刻得到它并且继续进行。自旋锁可以在任何时刻防止多于一个的内核任务同时进入临界区,因此这种锁可有效地避免多处理器上并发运行的内核任务竞争共享资源。

linux自旋锁、互斥锁、信号量

|▌冷眼眸甩不掉的悲伤 提交于 2020-02-24 16:47:00
为了避免并发,防止竞争。内核提供了一组同步方法来提供对共享数据的保护。 我们的重点不是介绍这些方法的详细用法,而是强调为什么使用这些方法和它们之间的差别。 Linux 使用的同步机制可以说从2.0到2.6以来不断发展完善。从最初的原子操作,到后来的信号量,从大内核锁到今天的自旋锁。这些同步机制的发展伴随 Linux从单处理器到对称多处理器的过度;伴随着从非抢占内核到抢占内核的过度。锁机制越来越有效,也越来越复杂。 目前来说内核中原子操作多用来做计数使用,其它情况最常用的是两种锁以及它们的变种:一个是自旋锁,另一个是信号量。我们下面就来着重介绍一下这两种锁机制。 自旋锁 ------------------------------------------------------ 自旋锁是专为防止多处理器并发而引入的一种锁,它在内核中大量应用于中断处理等部分(对于单处理器来说,防止中断处理中的并发可简单采用关闭中断的方式,不需要自旋锁)。 自旋锁最多只能被一个内核任务持有,如果一个内核任务试图请求一个已被争用(已经被持有)的自旋锁,那么这个任务就会一直进行忙循环——旋转——等待锁重新可用。要是锁未被争用,请求它的内核任务便能立刻得到它并且继续进行。自旋锁可以在任何时刻防止多于一个的内核任务同时进入临界区,因此这种锁可有效地避免多处理器上并发运行的内核任务竞争共享资源。 事实上

我们常说的 CAS 自旋锁是什么

[亡魂溺海] 提交于 2020-02-23 03:27:57
https://www.cnblogs.com/fengzheng/p/9018152.html https://www.jianshu.com/p/f714c440d0cb CAS在jdk应用 ConcurrentHashMap1.7使用lock锁实现,1.8使用cas+synchronized CAS(Compare and swap),即比较并交换,也是实现我们平时所说的自旋锁或乐观锁的核心操作。 它的实现很简单,就是用一个预期的值和内存值进行比较,如果两个值相等,就用预期的值替换内存值,并返回 true。否则,返回 false。 保证原子操作 任何技术的出现都是为了解决某些特定的问题, CAS 要解决的问题就是保证原子操作。原子操作是什么,原子就是最小不可拆分的,原子操作就是最小不可拆分的操作,也就是说操作一旦开始,就不能被打断,直到操作完成。在多线程环境下,原子操作是保证线程安全的重要手段。举个例子来说,假设有两个线程在工作,都想对某个值做修改,就拿自增操作来说吧,要对一个整数 i 进行自增操作,需要基本的三个步骤: 1、读取 i 的当前值; 2、对 i 值进行加 1 操作; 3、将 i 值写回内存; 假设两个进程都读取了 i 的当前值,假设是 0,这时候 A 线程对 i 加 1 了,B 线程也 加 1,最后 i 的是 1 ,而不是 2。这就是因为自增操作不是原子操作

java锁(转)

感情迁移 提交于 2020-02-17 17:13:23
Java中锁分类 锁的分类 公平锁/非公平锁 可重入锁 独享锁/共享锁 互斥锁/读写锁 乐观锁/悲观锁 分段锁 偏向锁/轻量级锁/重量级锁 自旋锁(java.util.concurrent包下的几乎都是利用锁) CAS 它是解决轻微冲突的多线程场景下使用锁造成性能损耗的 一种机制 。先是 比较 ,如果不符合预期,则 重试 。它有三个操作因素: 内存位置 , 预期原值 与 新值 。如果内存位置的值与预期原值相等,则处理器将该位置值更新为新值,如果不相等,则获取当前值,然后进行不断的轮询操作直到成果达到某个阙值退出。 AQS AbstractQueuedSynchronizer 简称AQS是一个抽象同步框架,可以用来实现一个依赖状态的同步器。JDK1.5中提供的 java.util.concurrent 包中的大多数的同步器 (Synchronizer) 如 Lock, Semaphore, Latch, Barrier 等,它们都是基于 java.util.concurrent.locks.AbstractQueuedSynchronizer 这个类的框架实现的。 乐观锁/悲观锁 乐观锁 :乐观锁是一种乐观思想,认为 读多写少 ,遇到并发的可能性低,每次拿数据时候并 不会上锁 ,因为认为不会被别人修改。但是更新的时候会判断有没有人会更新这条数据,采取写的时候先 读取版本号然后加锁

自旋锁问题

狂风中的少年 提交于 2020-02-13 15:23:16
1.自旋锁定义 自旋锁是转为防止多处理器并发而引入的一种锁,它在内核中大量应用与中断处理等部分(对于单处理器,防治中断处理的并发可简单采用关闭中断的方式,即在标志寄存器中关闭/打开中断标志位,不需要自旋锁)。自旋锁与互斥锁的区别:(1)对于互斥所,若资源已被占用,资源申请者只能进入睡眠状态;(2)但自旋锁不会引起调用者睡眠,若自旋锁已被别的执行单元保持,调用者就一直循环查看是否该自旋锁的保持者已释放了锁。 2.原理 跟 互斥锁 一样,一个执行单元要想访问被自旋锁保护的 共享资源 ,必须先得到锁,在访问完共享资源后,必须释放锁。如果在获取自旋锁时,没有任何执行单元保持该锁,那么将立即得到锁;如果在获取自旋锁时锁已经有保持者,那么获取锁操作将自旋在那里,直到该自旋锁的保持者释放了锁。 3.存在的问题 (1) 死锁 。试图递归地获得自旋锁必然会引起 死锁 :递归程序的持有实例在第二个实例循环,以试图获得相同自旋锁时,不会释放此自旋锁。在递归程序中使用自旋锁应遵守下列策略:递归程序决不能在持有自旋锁时调用它自己,也决不能在 递归调用 时试图获得相同的自旋锁。此外如果一个进程已经将资源锁定,那么,即使其它申请这个资源的进程不停地疯狂“自旋”,也无法获得资源,从而进入 死循环 。 (2)过多占用 cpu 资源。如果不加限制,由于申请者一直在循环等待,因此自旋锁在锁定的时候,如果不成功,不会睡眠

longAdd

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

Java多线程(二)

最后都变了- 提交于 2020-02-12 11:33:30
个人博客 http://www.milovetingting.cn Java多线程(二) 前言 本文为学习Java相关知识所作笔记,参考以下资料: https://github.com/Snailclimb/JavaGuide ,感谢原作者的分享! JAVA 锁 乐观锁 乐观锁是一种乐观思想,即认为读多写少,遇到并发写的可能性低,每次去拿数据的时候都认为别人不会修改,所以不会上锁,但是在更新的时候会判断一下在此期间别人有没有去更新这个数据,采取在写时先读出当前版本号,然后加锁操作(比较跟上一次的版本号,如果一样则更新),如果失败则要重复读-比较-写的操作。 java 中的乐观锁基本都是通过 CAS 操作实现的,CAS 是一种更新的原子操作,比较当前值跟传入值是否一样,一样则更新,否则失败。 悲观锁 悲观锁是就是悲观思想,即认为写多,遇到并发写的可能性高,每次去拿数据的时候都认为别人会修改,所以每次在读写数据的时候都会上锁,这样别人想读写这个数据就会 block 直到拿到锁。java中的悲观锁就是Synchronized,AQS框架下的锁则是先尝试cas乐观锁去获取锁,获取不到,才会转换为悲观锁,如 RetreenLock。 自旋锁 自旋锁原理非常简单,如果持有锁的线程能在很短时间内释放锁资源,那么那些等待竞争锁的线程就不需要做内核态和用户态之间的切换进入阻塞挂起状态