自旋

synchronized实现原理

一个人想着一个人 提交于 2020-03-23 22:12:53
3 月,跳不动了?>>> 目前在Java中存在两种锁机制:synchronized和Lock。数据同步需要依赖锁,那锁时如何实现的呢,synchronized给出的答案是软件层面依赖JVM,而Lock给出的方案是在硬件层面依赖特殊的CPU指令。 下面先介绍synchronized的实现: synchronized 是关键字,即使有了Lock接口(Lock能够实现synchronized能够实现的所有功能),使用的还是非常广泛。其应用层的语义是可以把任何一个非nul对象作为锁,当synchronized作用在方法上时,锁住的对象是实例对象(this);当作用在静态方法时锁住的对象时对象对应的Class实例,因为Class数据存在于永久带,因此静态方法锁相当于类的一个全局锁;当synchronized作用于某一个对象实例时,锁住的便是对应的代码块。在JVM实现中,所有一个专门的名字:对象监视器 线程状态与状态的转换 当多个线程同时请求某个对象监视器时,对象监视器会设置几种状态来区分请求的线程 Contention List:所有请求锁的线程将被首先放置到竞争队列。Contention List是一个虚拟队列,原因在于Contention List是一个由Node及next指针逻辑构成,并不存在一个Queue的数据结构。ContentionList是一个后进先出(LIFO)队列。

死磕 java集合之SynchronousQueue源码分析

隐身守侯 提交于 2020-03-23 16:23:15
问题 (1)SynchronousQueue的实现方式? (2)SynchronousQueue真的是无缓冲的吗? (3)SynchronousQueue在高并发情景下会有什么问题? 简介 SynchronousQueue是java并发包下无缓冲阻塞队列,它用来在两个线程之间移交元素,但是它有个很大的问题,你知道是什么吗?请看下面的分析。 源码分析 主要属性 // CPU的数量 static final int NCPUS = Runtime.getRuntime().availableProcessors(); // 有超时的情况自旋多少次,当CPU数量小于2的时候不自旋 static final int maxTimedSpins = (NCPUS < 2) ? 0 : 32; // 没有超时的情况自旋多少次 static final int maxUntimedSpins = maxTimedSpins * 16; // 针对有超时的情况,自旋了多少次后,如果剩余时间大于1000纳秒就使用带时间的LockSupport.parkNanos()这个方法 static final long spinForTimeoutThreshold = 1000L; // 传输器,即两个线程交换元素使用的东西 private transient volatile Transferer<E>

一个自旋锁的栗子

大兔子大兔子 提交于 2020-03-21 17:27:13
  一直以来不是怎么清楚自旋锁,最近有点时间,好好的学习了一下;   所谓的自旋锁在我的理解就是多个线程在尝试获取锁的时候,其中一个线程获取锁之后,其他的线程都处在一直尝试获取锁的状态,不会阻塞!!!那么什么叫做一直尝试获取锁呢?就是一个循环,比较经典的是AtomicInteger中的一个updateAndGet方法,下图所示(当然也可以直接看unsafe类中的getAndAddInt等类似方法);   我们可以看出在while循环中使用CAS去尝试更新一个变量,如果更新失败,就会一直在这个循环中一直在尝试;成功的话,就可以到最后的return语句;   由此我们可以大概知道如果自旋的线程过多,那么CPU的资源就会被大量消耗!!!   顺便提一个东西叫做原子引用,官方提供了AtomicInteger,AtomicBoolean等原子类,那么如果我们自己定义的类也需要有原子性怎么办呢?所以官方提供了一个AtomicReference类,可以将我们自己定义的类封装一下,就成了我们自己的原子类,例如AtomicReference<Student> atomicReference = new AtomicReference<>();,然后我们对Student的实例进行CAS各种CAS操作;   栗子: package TestMain; import lombok.extern.slf4j

多线程 -- CAS自旋锁、Atomic类

廉价感情. 提交于 2020-03-18 17:01:03
1、CAS(compare and swap) CAS 概念: CAS是一种系统原语,能够原子地完成比较和交换两个动作(所谓原语属于操作系统用语范畴。原语由若干条指令组成的,用于完成一定功能的一个过程。primitive or atomic action 是由若干个机器指令构成的完成某种特定功能的一段程序,具有不可分割性·即原语的执行必须是连续的,在执行过程中不允许被中断)。CAS是Compare And Set的缩写。CAS有3个操作数,内存值V,旧的预期值E,要修改的新值N。当且仅当预期值E和内存值V相同时,将内存值V修改为B,否则什么都不做。 CAS 执行流程图: 2、自旋锁 (spinlock) 前言: 在介绍自旋锁之前,需要提到关键字 Volatile,它可以保证 JMM 模型所规定的可见性+有序性,但是它却不能保证原子性,即如果用它是不能保证对修饰变量操作的原子性的。因此不能用它来作为锁,但是它配合上 CAS 就可以构造出一种“无锁策略”的乐观自旋锁,保证了操作的原子性,而且在适合其应用的场景下效率是比较高的,因为它的优点就是可以避免线程的上下文切换所带来的系统开销。 概念: 是指当一个线程在获取锁的时候,如果锁已经被其它线程获取,那么该线程将循环等待,然后不断的判断锁是否能够被成功获取,直到获取到锁才会退出循环。 自旋锁缺点: 1)如果某个线程持有锁的时间过长

Java多线程系列--利用自旋原理来无锁实现“只创建一次”的场景

自古美人都是妖i 提交于 2020-03-11 10:09:21
相信看过之前几篇自旋锁实现的同学对设计一个自旋锁会有一定的感觉,有几个实现的要点很实用: 1. 使用AtomicBoolean原子变量的getAndSet(true)方法来实现并发情况下,找到第一个成功执行方法的线程。这个技巧经常使用,在并发编程中经常会遇到这种需求 2.经常会使用volatile boolean类型的变量在多个线程之间同步状态,需要注意的是,对volatile变量的修改只具有可见性,不具有原子性(比如++操作)。 3. 使用一个AtomicReference原子变量的getAndSet方法来创建一个虚拟的链表结构,原理也是CAS操作,在队列锁中经常使用 这篇结合一个实例来说说如何灵活地利用这些技巧实现无锁的能力。 在实际开发中会经常遇到“只创建一次”的场景,这里说的“只创建一次”不是说单实例模式。单实例模式也是解决只创建一次的问题,关于单实例模式有很多技巧来实现高并发情况下只创建一个对象的问题,这里不讨论。 单实例模式解决的问题是所有线程都公用一个静态的引用,可以用volatile变量来标示这个静态引用,从而在所有线程之间共享这个静态引用的状态是否已经改变。 这里说的“只创建一次”的场景是这个需要创建一次的对象不是直接被全局的引用所引用,而是间接地被引用。经常有这种情况,全局维护一个并发的ConcurrentMap, Map的每个Key对应一个对象

乐观锁与悲观锁、自旋锁

做~自己de王妃 提交于 2020-03-11 03:33:08
乐观锁 乐观锁是一种乐观的思想,即认为读多写少,遇到并发的可能性低, 每次拿数据时都认为别人不会修改,所以不会上锁,但是在更新的时候会判断一下在此期间别人有没有去更新这个数据 ,可以使用版本号机制和 CAS 算法实现。 Java 中的乐观锁基本都是通过 CAS 操作实现的, CAS 是一种更新的原子操作,比较当前值跟传入值是否一样,一样则更新,否则失败。 悲观锁 悲观锁就是悲观的思想,即认为写多,遇到并发的可能性高,每次拿数据时,都会认为别人会修改数据,所以在每次读数据的时候都会上锁,这样当别人想读写这个数据时就会阻塞,直到拿到锁。 (共享资源每次只给一个线程使用,其它线程阻塞,用完后再把资源转让给其它线程) 。传统的关系型数据库里边就用到了很多这种锁机制,比如行锁,表锁等,读锁,写锁等,都是在做操作之前先上锁。 Java 中的悲观锁就是 Synchronized,AQS 框架下的锁则是先尝试 CAS 乐观锁去获取锁,获取不到,才会转换为悲观锁,如 ReentrantLock。 乐观锁的缺点 ABA 问题 CAS 会导致 “ABA 问题”。CAS 算法实现的一个重要前提是需要取出内存中某时刻的数据,而在下一时刻比较并替换,那么在这个时间差会导致数据的变化。 比如说一个线程 one 从内存位置 V 中取出 A,这时候另一个线程 two 也从内存中取出 A,并且 two

java锁学习

為{幸葍}努か 提交于 2020-03-08 08:04:38
锁从设计理念上可分为2类,分别为悲观锁(互斥锁)和乐观锁(非互斥锁) 悲观锁适用于写多读少的场景,乐观锁适用于读多写少的场景 java中的悲观锁就是Synchronized,AQS框架下的锁则是先尝试cas乐观锁去获取锁,获取不到,才会转换为悲观锁,如RetreenLock。 java中主要锁有2种实现方式,分别是jvm虚拟机实现的(Synchronized关键字)和JDK 代码实现的(Lock接口实现等java.util.concurrent.locks包下的锁) Synchronized实现的锁是一种互斥锁(一次最多只能有一个线程持有的锁,当一个线程持有该锁的时候其它线程无法进入上锁的区域),它是一种悲观锁 synchronized最早只有重量级锁,在jdk1.6中对Synchronized进行了优化。 在编译器层面,使用了锁消除(对一些没有必要的、不会引起安全问题的同步代码取消同步)和锁粗化(对那些多次执行同步的代码且它们可以可并到一次同步的代码) 同时引进了适应性自旋锁,偏向锁,轻量级锁,这3种锁属于乐观锁。 jdk1.6中是默认开启偏向锁和轻量级锁的 Java SE1.6里锁一共有四种状态,无锁状态,偏向锁状态,轻量级锁状态和重量级锁状态,它会随着竞争情况逐渐升级。 锁可以升级但不能降级,这种锁升级却不能降级的策略,目的是为了提高获得锁和释放锁的效率。 偏向锁:(Mark

单例模式、读写锁

喜夏-厌秋 提交于 2020-03-02 10:18:06
单例模式 是设计模式中的一种,是大佬们针对典型场景设计的解决方案。 典型场景:一个对象/一个资源 只能被初始化加载一次 。 例如:你在打游戏的时候,游戏有很多图片资源。希望一个图片只加载一次。 实现方式 : 饿汉/懒汉 饿汉:所有资源在程序初始化阶段 一次性 完成初始化(资源在初始化的时候一次性全部加载,后续只需要使用就可以) template<class T> class single { static T _data;//所以实例化的对象共用同一份资源 T* get_instance() { return &_data; } } 资源初始化的时候会慢一点,但是运行起来以后,会很流畅。 懒汉:资源在 使用的时候 进行初始化(用到的时候再去加载,当然也要保证只加载一次) template <typename T> class Singleton { static T* inst; public: static T* GetInstance() { if (inst == NULL) { inst = new T(); } return inst; } } 在使用的时候加载一次资源,会涉及到线程安全问题。(volatile、static、mutex、二次判断) 使用static保证多个对象使用 同一份 空间资源。(保证资源只被加载一次,实现单例模式) 加锁保护资源申请过程

偏向锁、轻量级锁、重量级锁区别与联系

百般思念 提交于 2020-03-01 05:53:55
今天总结了锁升级(偏向锁、轻量级锁、重量级锁)和锁优化下面开始总结。 其实这些内容都是JVM对锁进行的一些优化,为什么分开讲,原因是锁升级比较重要,也比较难。 一、锁升级 在1.6之前java中不存在只存在重量级锁,这种锁直接对接底层操作系统中的互斥量(mutex),这种同步成本非常高,包括操作系统调用引起的内核态与用户态之间的切换。线程阻塞造成的线程切换等。因此在jdk 1.6中将锁分为四种状态:由低到高分别为:无锁状态、偏向锁状态、轻量级锁状态、重量级锁状态。 1. 偏向锁。什么是偏向锁呢?为什么要引入偏向锁呢? 偏向锁是如果一个线程获取到了偏向锁,在没有其他线程竞争的情况下,如果下次再执行该同步块时则只需要简单判断当前偏向锁所偏向的对象是否是当前线程,如果是则不需要再进行任何获取锁与释放锁的过程,直接执行同步块。至于为什么引入偏向锁,是因为经过JVM的开发人员大量的研究发现大多数时候都是不存在锁竞争的,通常都是一个线程在使用锁的时候没有其他线程来竞争,然而每次都要进行加锁和解锁就会额外增加一些没有必要的资源浪费。为了降低这些浪费,JVM引入了偏向锁。 a) 偏向锁的获取以及升级过程如下: 当一个线程在执行同步块时,它会先获取该对象头的MarkWord,通过MarkWord来判断当前虚拟机是否支持偏向锁(因为偏向锁是可以手动关闭的),如果不支持则直接进入轻量级锁获取过程

STT-MRAM万能存储器芯片

只愿长相守 提交于 2020-02-28 01:05:33
传统存储器的技术局限以及不断缩小的制造尺寸所带来的巨大挑战促使科研人员开始寻找新一代存储器件,它应具有接近静态存储器的纳秒级读写速度,具有动态存储器甚至闪存级别的集成密度和类似Flash的非易失性存储特性。 “万能存储器”概念作为新一代存储器的要求被提出来。自旋转移矩—磁随机存储器器件(Spin Transfer Torque - Magnetic RandomAccess Memory:STT-MRAM)就是一种接近“万能存储器”要求的极具应用潜力的下一代新型存储器解决方案。 类比地球的公转与自转,微观世界的电子同时具有围绕原子核的“公转”轨道运动(电荷属性)、电子内禀运动(自旋属性)。 STT-MRAM 就是一种可以同时操纵电子电荷属性及自旋属性的存储器件。1988年,法国阿尔贝·费尔和德国彼得·格林贝格研究员通过操纵电子自旋属性实现了基于电子自旋效应的磁盘读头,使磁盘容量在20年间从几十兆比特(MB)暴增到几太比特(TB)。他们因此获得2007年的诺贝尔物理奖。 在读操作方面,磁随机存储器一般基于隧穿磁阻效应,在铁磁层1/绝缘层/铁磁层2三层结构中,当两层铁磁层磁化方向相同时,器件呈现“低电阻状态”,当两层铁磁层磁化方向相反时,器件呈现“高电阻状态”,且两个状态可以相互转化;在写操作方面,基于自旋转移矩效应,器件处于高阻态时,通自上而下的电流