自旋

自旋锁、互斥锁

≯℡__Kan透↙ 提交于 2019-12-05 23:05:55
自旋锁(spinlock):是指当一个线程在获取锁的时候,如果锁已经被其它线程获取,那么该线程将循环等待,然后不断的判断锁是否能够被成功获取,直到获取到锁才会退出循环。 它是为实现保护共享资源而提出一种锁机制。其实,自旋锁与互斥锁比较类似,它们都是为了解决对某项资源的互斥使用。无论是互斥锁,还是自旋锁,在任何时刻,最多只能有一个保持者,也就说,在任何时刻最多只能有一个执行单元获得锁。但是两者在调度机制上略有不同。对于互斥锁,如果资源已经被占用,资源申请者只能进入睡眠状态。但是自旋锁不会引起调用者睡眠,如果自旋锁已经被别的执行单元保持,调用者就一直循环在那里看是否该自旋锁的保持者已经释放了锁,”自旋”一词就是因此而得名。 来源: https://www.cnblogs.com/macht/p/11949609.html

各类锁(互斥锁,自旋锁,读写锁,乐观锁,悲观锁,死锁)

拜拜、爱过 提交于 2019-12-05 19:06:08
互斥锁 当有一个线程要访问共享资源(临界资源)之前会对线程访问的这段代码(临界区)进行加锁。如果在加锁之后没释放锁之前其他线程要对临界资源进行访问,则这些线程会被阻塞睡眠,直到解锁,如果解锁时有一个或者多个线程阻塞,那么这些锁上的线程就会变成就绪状态,然后第一个变为就绪状态的线程就会获取资源的使用权,并且再次加锁,其他线程继续阻塞等待。 读写锁 也叫做共享互斥锁,读模式共享,写模式互斥。有点像数据库负载均衡的读写分离模式。它有三种模式:读加锁状态,写加锁状态和不加锁状态。简单来说就是只有一个线程可以占有写模式的读写锁,但是可以有多个线程占用读模式的读写锁。 当写加锁的模式下,任何线程对其进行加锁操作都会被阻塞,直到解锁。 当在读加锁的模式下,任何线程都可以对其进行读加锁的操作,但所有试图进行写加锁操作的线程都会被阻塞。直到所有读线程解锁。但是当读线程太多时,写线程一直被阻塞显然是不对的,所以一个线程想要对其进行写加锁时,就会阻塞读加锁,先让写加锁线程加锁 自旋锁 自旋锁和互斥锁很像,唯一不同的是自旋锁访问加锁资源时,会一直循环的查看是否释放锁。这样要比互斥锁效率高很多,但是只会占用CPU。所以自旋锁适用于多核的CPU。但是还有一个问题是当自旋锁递归调用的时候会造成死锁现象。所以慎重使用自旋锁。 乐观锁 这其实是一种思想,当线程去拿数据的时候,认为别的线程不会修改数据,就不上锁

多线程编程2.0——锁的相关认识

佐手、 提交于 2019-12-05 09:07:38
多线程编程2.0——锁的相关认识 多线程对于共享变量访问带来的安全性问题 数据结果与预期不一致性的问题 问题描述 一个自增的程序,当两个线程操作时,可能读取的时同一个数据,再将其自增1,最终返回值只增加了1,实际结果应该增加2。 解决方法 增加锁,达到数据安全的目的,不允许读正在被修改的数据,只允许读没有被修改状态的数据。 锁的认识 Java提供的加锁方法就是Synchroinzed关键字 背景 在多线程并发编程中synchronized一直是元老级角色,很 多人都会称呼它为重量级锁。但是,随着 Java SE 1.6 对 synchronized进行了各种优化之后,有些情况下它就并不 那么重,Java SE 1.6中为了减少获得锁和释放锁带来的性 能消耗而引入的偏向锁和轻量级锁 加锁的方式 修饰实例方法,作用于当前实例加锁,进入同步代码前 要获得当前实例的锁 静态方法,作用于当前类对象加锁,进入同步代码前要 获得当前类对象的锁 修饰代码块,指定加锁对象,对给定对象加锁,进入同 步代码库前要获得给定对象的锁,不同的修饰类型,代表锁的控制粒度 synchronized锁是如何存储 观察 synchronized的整个语法发现,synchronized(lock)是基于 lock 这个对象的生命周期来控制锁粒度的 对象在内存中的布局 在 Hotspot 虚拟机中,对象在内存中的存储布局

【并发编程】synchronized的使用场景和原理简介

丶灬走出姿态 提交于 2019-12-05 04:38:58
1. synchronized使用 1.1 synchronized介绍 在多线程并发编程中synchronized一直是元老级角色,很多人都会称呼它为重量级锁。但是,随着Java SE 1.6对synchronized进行了各种优化之后,有些情况下它就并不那么重了。 synchronized可以修饰普通方法,静态方法和代码块。 当synchronized修饰一个方法或者一个代码块的时候,它能够保证在同一时刻最多只有一个线程执行该段代码。 对于普通同步方法,锁是当前实例对象(不同实例对象之间的锁互不影响)。 对于静态同步方法,锁是当前类的Class对象。 对于同步方法块,锁是Synchonized括号里配置的对象。 当一个线程试图访问同步代码块时,它首先必须得到锁,退出或抛出异常时必须释放锁。 1.2 使用场景 synchronized最常用的使用场景就是多线程并发编程时线程的同步。这边还是举一个最常用的列子:多线程情况下银行账户存钱和取钱的列子。 public class SynchronizedDemo { public static void main(String[] args) { BankAccount myAccount = new BankAccount("accountOfMG",10000.00); for(int i=0;i<100;i++){ new

Java中的锁

别来无恙 提交于 2019-12-05 02:55:31
一、乐观锁   乐观锁是一种乐观思想,即认为读多写少,遇到并发写的可能性低,每次去拿数据时都认为别人不会修改,所以不会上锁,但是在更新时会判断一下在此期间别人有没有去更新这个数据,采取在写时先读出当前版本号,然后加锁操作(比较跟上一次的版本号,如果一样则更新),如果失败则要重复读-比较-写操作。   Java中的乐观锁基本是通过CAS操作实现的,CAS是一种更新的原子操作,比较当前值跟传入值是否一样,一样则更新,否则失败。 二、悲观锁   悲观锁就是悲观思想,即认为写多,遇到并发写的可能性高,每次去拿数据时都认为被人会修改,所以每次在读写数据时都会上锁。这样别人想读写这个数据就会block知道拿到锁。Java中的悲观所就是Synchronized,AQS框架下的锁则是先尝试CAS乐观锁去获取锁,若获取不到时,才会转换为悲观锁,如RetreenLock。 三、Synchronized同步锁 可以把任意一个非null的对象当作锁,它属于独占式的悲观锁,同时属于可重入锁。 作用范围:   1)作用于方法时,锁住的是对象的实例(this);   2)作用于静态方法时,锁住的是Class实例,又因为Class的相关数据存储在永久代PermGen(JDK1.8则是metaspace),永久代是全局共享的,因此静态方法相当于类的一个全局锁,会锁所有调用该方法的线程。   3

[内核同步]自旋锁spin_lock、spin_lock_irq 和 spin_lock_irqsave 分析【转】

a 夏天 提交于 2019-12-05 01:49:50
转自: https://www.cnblogs.com/x_wukong/p/8573602.html 转自;https://www.cnblogs.com/aaronLinux/p/5890924.html 自旋锁的初衷 :在短期间内进行轻量级的锁定。一个被争用的自旋锁使得请求它的线程在等待锁重新可用的期间进行自旋(特别浪费处理器时间),所以自旋锁不应该被持有时间过长。如果需要长时间锁定的话, 最好使用信号量。 单处理器的自旋锁 : 首先,自旋锁的目的如果在系统不支持内核抢占时,自旋锁的实现也是空的,因为单核只有一个线程在执行,不会有内核抢占,从而资源也不会被其他线程访问到。 其次,支持内核抢占,由于自旋锁是禁止抢占内核的,所以不会有其他的进程因为等待锁而自旋. 最后,只有在多cpu下,其他的cpu因为等待该cpu释放锁,而处于自旋状态,不停轮询锁的状态。所以这样的话,如果一旦自旋锁内代码执行时间较长,等待该锁的cpu会耗费大量资源,也是不同于信号量和互斥锁的地方。 简单来说,自旋锁在内核中主要用来防止多处理器中并发访问临界区,防止内核抢占造成的竞争。 自旋锁内睡眠禁止睡眠问题 :如果自旋锁锁住以后进入睡眠,而此时又不能进行处理器抢占(锁住会disable prempt),其他进程无法获得cpu,这样也不能唤醒睡眠的自旋锁,因此不相应任何操作。 自旋锁为什么广泛用于内核

Linux中自旋锁

▼魔方 西西 提交于 2019-12-04 07:05:51
传统的spinlock   Linux的的内核最常见的锁是自旋锁。自旋锁最多只能被一个可执行线程持有。如果一个执行线程试图获得一个被已经持有(争用)的自旋锁,那么该线程就会一直进行忙循环-旋转-等待锁重新可用要是锁未被争用,请求锁的执行线程就可以立即得到它,继续执行。在任意时间,自旋锁都可以防止多于一个的执行线程同时进入临界区。同一个锁可以用在多个位置,例如,对于给定数据的所有访问都可以得到保护和同步。   自旋锁在同一时刻至多被一个执行线程持有,所以一个时刻只有一个线程位于临界区内,这就为多处理器机器提供了防止并发访问所需的保护机制。在单处理机器上,编译的时候不会加入自旋锁,仅会被当作一个设置内核抢占机制是否被启用的开关。如果禁止内核抢占,那么在编译时自旋锁就会被剔除出内核。   传统的自旋锁本质上用一个整数来表示,值为1代表锁未被占用, 为0或者为负数表示被占用。   在 单处理机环境 中可以使用特定的原子级汇编指令 swap 和 test_and_set 实现进程互斥,(Swap指令:交换两个内存单元的内容;test_and_set指令取出内存某一单元(位)的值,然后再给该单元(位)赋一个新值) 这些指令涉及对同一存储单元的两次或两次以上操作,这些操作将在几个指令周期内完成,但由于中断只能发生在两条机器指令之间,而同一指令内的多个指令周期不可中断

深入Synchronized的实现原理与源码分析

Deadly 提交于 2019-12-04 04:36:59
前言 一、synchronized的特性 1.1 原子性 1.2 可见性 1.3 有序性 1.4 可重入性 二、synchronized的用法 三、synchronized锁的实现 3.1 同步方法 3.2 同步代码块 四、synchronized锁的底层实现 五、JVM对synchronized的优化 5.1 锁膨胀 5.1.1 偏向锁 5.1.2 轻量级锁 5.1.3 重量级锁 5.2 锁消除 5.3 锁粗化 5.4 自旋锁与自适应自旋锁 结语 前言 如果某一个资源被多个线程共享,为了避免因为资源抢占导致资源数据错乱,我们需要对线程进行同步,那么synchronized就是实现线程同步的关键字,可以说在并发控制中是必不可少的部分,今天就来看一下synchronized的使用和底层原理。 一、synchronized的特性 1.1 原子性 所谓原子性就是指一个操作或者多个操作,要么全部执行并且执行的过程不会被任何因素打断,要么就都不执行。 在Java中,对基本数据类型的变量的读取和赋值操作是原子性操作,即这些操作是不可被中断的,要么执行,要么不执行。但是像i++、i+=1等操作字符就不是原子性的,它们是分成 读取、计算、赋值 几步操作,原值在这些步骤还没完成时就可能已经被赋值了,那么最后赋值写入的数据就是脏数据,无法保证原子性。

Java 高效并发

元气小坏坏 提交于 2019-12-04 00:10:53
Java 高效并发 为了便于移植,Java 多线程内存模型不与硬件关联,不同硬件平台可以使用不同的实现手段 和 CPU 内存与高速缓存做对比 Java 内存模型被分为两大部分: 主内存 (对应 PC 内存)和 工作内存 (对应 CPU 高速缓存) 主内存与工作内存之间数据的交互 Java 定义了以下 8 种 原子 操作(最新的 Java 标准已经采用了新的内存访问协议,但下面 8 中操作也应该了解一下) lock,标识主内存变量为线程独占 同一个变量可以被一条线程多次 lock,但也需要同样次数的 unlock 才能解锁 lock 一个变量时会清空工作内存中此变量的值,在使用这个变量前需要执行 load 和 assign 初始化变量 unlock,释放主内存被锁变量 一个变量实现没有被 lock,那也不允许执行 unlock 对一个变量执行 unlock 之前,必须先把变量同步到主内存中 read,从主内存中读取变量到工作内存中,以便后续的 load 操作 load,作用于工作内存,把 read 操作从主内存中得到的变量值放入工作内存的变量副本中 use,把工作内存中的变量传递给执行引擎 只有对一个变量前一个操作是 load 时,当前线程才可以使用 use;只有对一个变量后一个操作是 use 时才可以对变量执行 load 操作 assign

自旋锁、阻塞锁、重入锁、偏向锁、轻量锁和重量锁

笑着哭i 提交于 2019-12-03 20:16:37
1、自旋锁: 采用让当前线程不停的在循环体内执行实现,当循环的条件被其它线程改变时才能进入临界区 举例如下: 优缺点分析: 由于自旋锁只是将当前线程不停地执行循环体,不进行线程状态的改变,所以响应速度更快。但当线程数不停增加时,性能下降明显,因为每个线程都需要执行,占用CPU时间。如果线程竞争不激烈,并且保持锁的时间段。适合使用自旋锁。 大家可以点击加群【JAVA架构知识学习讨论群】473984645,(如多你想跳槽换工作,但是技术又不够,或者工作遇到了瓶颈,我这里有一个Java的免费直播课程,讲的是高端的知识点,只要有1-5年的开发工作经验可以加群找我要课堂链接。)注意:是免费的 没有开发经验的误入。 2、阻塞锁 : 阻塞锁改变了线程的运行状态,让线程进入阻塞状态进行等待,当获得相应的信号(唤醒或者时间)时,才可以进入线程的准备就绪状态,转为就绪状态的所有线程,通过竞争,进入运行状态。 优缺点分析: 阻塞锁的优势在于,阻塞的线程不会占用cpu时间,不会导致 CPu占用率过高,但进入时间以及恢复时间都要比自旋锁略慢。在竞争激烈的情况下 阻塞锁的性能要明显高于自旋锁。 3、重入锁 : Java中的synchronized同步块是可重入的。这意味着如果一个java线程进入了代码中的synchronized同步块,并因此获得了该同步块使用的同步对象对应的管程上的锁