自旋锁

深入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等操作字符就不是原子性的,它们是分成 读取、计算、赋值 几步操作,原值在这些步骤还没完成时就可能已经被赋值了,那么最后赋值写入的数据就是脏数据,无法保证原子性。

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

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

synchronized和lock

匿名 (未验证) 提交于 2019-12-03 00:11:01
类别 synchronized Lock 存在层次 Java的关键字 一个类 锁的释放 1.以获取锁的线程执行代码同步代码,释放锁。 2,线程执行发生异常,jvm会让线程释放锁 在finally中必须释放锁,不然容易造成线程死锁 锁的获取 假设A线程获得锁,B线程等待,如果A线程阻塞,则B会一直等 分情况而定,Lock有多个获取锁的方式,线程不用一直等 锁状态 无法判断 可判断 锁类型 可重入,不可中断,非公平 可重入,可判断,可公平 性能 少量同步 大量同步 1.Lock Lock的几个方法 lock() 获取锁 unlock() 释放锁 trylock() 获得锁的状态,返回true和false tryLock(long time,TimeUnit unit) 比tryLock加了时间期限 lockInterruptibly() 2. synchronized 乐观锁 ( Optimistic Locking ) 相对悲观锁而言,乐观锁机制采取了更加宽松的加锁机制。悲观锁大多数情况下依靠数据库的锁机制实现,以保证操作最大程度的独占性。但随之而来的就是数据库性能的大量开销,特别是对长事务而言,这样的开销往往无法承受。而乐观锁机制在一定程度上解决了这个问题。乐观锁,大多是基于数据版本( Version )记录机制实现。何谓数据版本?即为数据增加一个版本标识

第七章 Linux设备驱动开发中的并发控制

匿名 (未验证) 提交于 2019-12-02 21:59:42
阅读笔记-个人记录 7.1并发和竞态 并发 :多个执行单元同时、并行被执行,而并发的执行单元对共享组员的访问很容易导致竞态 竞态发生情况: 1.对称多处理器(SMP)的多个CPU 2.单CPU内进程与抢占它的进程 一个进程在内核执行的时候可能耗完了自己的时间片,也可能被另一个高优先级进程打断,而进程与抢占它的进程访问共享资源的情况 3.中断与进程之间 中断可以打断正在执行的进程,且此时中断服务程序访问进程正在访问的资源,竞态也会发生;中断也有可能被更高优先级中断打断。 解决:保证对共享资源的互斥访问。即一个执行单元在访问共享资源时,其他执行单元被禁止访问 访问共享资源额代码区域成为临界区,需要某种互斥机制加以保护 7.2编译乱序和执行乱序 编译乱序:编译顺序不按照源代码中的顺序 解决:barrier()编译屏障“_asm__volatile_("":::"memory")” 执行乱序: 7.3 中断屏蔽 在进入临界区前屏蔽系统的中断,底层就是让CPU本身不响应中断。 7.4原子操作 7.5自旋锁 需先执行一个原子操作,该操作测试并设置某个内存变量,简单理解:把自旋锁当做一个变量看待,该变量把一个临界区标记为“我当前在运行,请稍等一会”或者是“我当前不在运行,可被使用” Linux自旋锁常用操作4种: (1)定义自旋锁 spinlock_t lock; (2)初始化 spin

Linux设备驱动程序 之 自旋锁

淺唱寂寞╮ 提交于 2019-12-02 19:30:30
概念 自旋锁可以再不能休眠的代码中使用,比如中断处理例程;在正确使用的情况下,自旋锁通常可以提供比信号量更高的性能; 一个自旋锁是一个互斥设备,它只能由两个值,锁定和解锁;通常实现为某个整数值中的单个位;希望获得特定锁的代码测试相关位,如果锁可用,则锁定位被设置,而嗲吗继续进入临界区;相反, 如果锁被其他人获得,则代码进入忙循环并重复检查这个锁,直到该锁可用为止 ;这循环就是自旋锁自旋的部分; 自旋锁在不同的架构上实现有所不同,但是核心概念低于所有系统都都是一样的,当存在某个自旋锁时,等待执行忙循环的处理器做不了任何有用的工作; 自旋锁最初是为了在多处理器系统上使用而设计的,考虑到并发问题, 单处理器工作站在运行可抢占内核时,其行为类似于SMP ;因为抢占,即使不打算在SMP系统上运行自己的嗲吗,我们仍然需要实现正确的锁定; 自旋锁和原子上下文 适用于自旋锁的规则是:任何拥有自旋锁的代码都必须是原子的,它不能休眠;事实上,它不能因为任何原因放弃处理器,除了服务中断以外(某些情况下此时也不能放弃处理器); 内核抢占的情况有自旋锁代码本身处理;任何时候,只要内核代码拥有自旋锁,在相关处理器上的抢占就会被禁止;甚至在单处理器上,也必须以同样的方式禁止抢占以避免竞态; 在拥有锁的时候避免休眠有时候很难做到,休眠可能发生在很多无法预期的地方,当我们编写需要在自旋锁下执行的代码时

iOS: 线程中那些常见的锁

拟墨画扇 提交于 2019-12-02 06:13:15
一、介绍 在多线程开发中,锁的使用基本必不可少,主要是为了解决资源共享时出现争夺而导致数据不一致的问题,也就是线程安全问题。锁的种类很多,在实际开发中,需要根据情况选择性的选取使用,毕竟使用锁也是消耗CPU的。 本人虽然一直有使用多线程进行开发,但是对于锁的使用和理解并不是特别的深入,这不看到一篇挺mark的博客: https://www.jianshu.com/p/a236130bf7a2 ,在此基础上稍添加点东西转载过来(尊重原创),一是为了记录便于随时翻阅,而是为了写一遍加深印象,知识都是一个copy和attract的过程。 二、种类 1、互斥锁 概念:对共享数据进行锁定,保证同一时刻只能有一个线程去操作。 抢到锁的线程先执行,没有抢到锁的线程就会被挂起等待。 等锁用完后需要释放,然后其它等待的线程再去抢这个锁,那个线程抢到就让那个线程再执行。 具体哪个线程抢到这个锁是由cpu调度决定的。 常用: @synchronized:同步代码块 example:执行操作 /** *设置属性值 */ -(void)setMyTestString:(NSString *)myTestString{ @synchronized(self) { // todo something _myTestString = myTestString; } } example:创建单例 //注意

自旋锁与互斥锁

人走茶凉 提交于 2019-12-02 05:53:20
自旋锁与互斥锁 理论分析 互斥锁的问题 自旋锁应用场景 自旋锁实践 总结 自旋锁与互斥锁 自旋锁和互斥锁是多线程程序中的重要概念。 它们被用来锁住一些共享资源, 以防止并发访问这些共享数据时可能导致的数据不一致问题。 但是它们的不同之处在哪里? 我们应该在什么时候用自旋锁代替互斥锁? 理论分析 从理论上说, 如果一个线程尝试加锁一个互斥锁的时候没有成功, 因为互斥锁已经被锁住了, 这个未获取锁的线程会休眠以使得其它线程可以马上运行。 这个线程会一直休眠, 直到持有锁的线程释放了互斥锁, 休眠的线程才会被唤醒。 如果一个线程尝试获得一个自旋锁的时候没有成功, 该线程会一直尝试加锁直到成功获取锁。 因此它不允许其它线程运行(当然, 操作系统会在该线程所在的时间片用完时, 强制切换到其它线程)。 互斥锁的问题 互斥锁存在的问题是, 线程的休眠和唤醒都是相当昂贵的操作, 它们需要大量的CPU指令, 因此需要花费一些时间。 如果互斥量仅仅被锁住很短的一段时间, 用来使线程休眠和唤醒线程的时间会比该线程睡眠的时间还长, 甚至有可能比不断在自旋锁上轮训的时间还长。自旋锁的问题是, 如果自旋锁被持有的时间过长, 其它尝试获取自旋锁的线程会一直轮训自旋锁的状态, 这将非常浪费CPU的执行时间, 这时候该线程睡眠会是一个更好的选择。 自旋锁应用场景 在单核/单CPU系统上使用自旋锁是没用的,

Java线程并发中常见的锁

大憨熊 提交于 2019-12-01 11:07:58
随着互联网的蓬勃发展,越来越多的互联网企业面临着用户量膨胀而带来的并发安全问题。本文着重介绍了在java并发中常见的几种锁机制。 1.偏向锁   偏向锁是JDK1.6提出来的一种锁优化的机制。其核心的思想是,如果程序没有竞争,则取消之前已经取得锁的线程同步操作。也就是说,若某一锁被线程获取后,便进入偏向模式,当线程再次请求这个锁时,就无需再进行相关的同步操作了,从而节约了操作时间,如果在此之间有其他的线程进行了锁请求,则锁退出偏向模式。在JVM中使用-XX:+UseBiasedLocking package jvmProject; import java.util.List; import java.util.Vector; public class Biased { public static List<Integer> numberList = new Vector<Integer> (); public static void main(String[] args) { long begin = System.currentTimeMillis(); int count = 0 ; int startnum = 0 ; while (count<10000000 ){ numberList.add(startnum); startnum +=2 ; count ++ ; }

Java并发:线程安全与锁优化

时间秒杀一切 提交于 2019-12-01 11:07:08
概述 人们很难想象现实中的对象在一项工作进行期间,会被不停地中断和切换,对象的属性(数据)可能会在中断期间被修改和变“脏”,而这些事情在计算机世界中则是很正常的事情。有时候,良好的设计原则不得不向现实做出一些让步,我们必须让程序在计算机中正确无误地运行,然后再考虑如何将代码组织得更好,让程序运行更快。对于“高效并发”来说,首先需要保证并发的正确性,然后在此基础上实现高效。 1.线程安全 《Java Concurrency In Practice》的作者Brian Goetz对“线程安全”有一个比较恰当的定义:“当多个线程访问一个对象时,如果不用考虑这些线程在运行时环境下的调度和交替执行,也不需要进行额外的同步,或者在调用方进行任何其他的协调操作,调用这个对象的行为都可以获得正确的结果,那这个对象是线程安全的”。 这个定义比较严谨,它要求线程安全的代码都必须具备一个特征:代码本身封装了所有必要的正确性保障手段(如互斥同步等),令调用者无须关心多线程的问题,更无须自己采取任何措施来保证多线程的正确调用。这点听起来简单,但其实并不容易做到,在大多数场景中,我们都会将这个定义弱化一些,如果把“调用这个对象的行为”限定为“单次调用”,这个定义的其他描述也能够成立的话,我们就可以称它是线程安全了。 1.1 Java语言中的线程安全 按照线程安全的“安全程度”由强至弱来排序

IRQL

我的梦境 提交于 2019-12-01 10:26:41
计算机组成原理:中断控制,寄存器,cpu,硬件中断与软件中断。硬件驱动与软驱动 https://blog.csdn.net/vc2014/article/details/45537849 KeAcquireSpinLock 申请自旋锁 linux内核同步 spin_lock 自旋锁 #ifdef WIN32 KeReleaseSpinLock(); #else spin_unlock(); #endif 来源: https://www.cnblogs.com/hshy/p/11681713.html