原子操作

非原子的64位数据操作(long和double)

妖精的绣舞 提交于 2020-03-06 15:40:45
非volatile类型的64位数值变量,在JVM的内存模型中写入和读出都是原子操作,但是64位的long和double类型变量在JVM的内存模型中,是将64位的操作拆分乘2个32位的操作。当取出一个非volatile类型的long或者double变量时,如果有一个读线程和一个写线程,那么读线程有可能读出一个原来数值的高32位和一个更改过后的低32位的数值。 来源: https://www.cnblogs.com/dazhu123/p/12426317.html

java高并发设计(一)--概念

可紊 提交于 2020-03-06 13:38:27
java高并发的核心,要了解并熟悉java的内存模型,并涉及相关的资源临界值。 java的内存模型围绕多线的技术要点:原子性,可见性,有序性。 原子性:原子性是指整个操作过的不可中断性,明确的说就是线程在执行过程中是顺序执行的,一旦开始操作将不受其他线程的干扰。比如对已经全局的变量进行复制的操作,每个线程都可以对其进行重新设值的操作,但是在多线程执行中,没有安全机制的情况下,不能保证其操作的原子性,有可能最终的结果不是你想看到的。所以最先的设置就是原子性。 可见性:可见性就是一个线程修改了某一个共享的变量或者资源,其他线程能够立即知道这个修改,可以参考java中的volatile关键字的效果。如果没有安全机制,一个全局变量的设置,在多线程执行下,一个线程对变量的修改对其他线程可能是不可见的,因为在多线程并发操作情况下,两个线程会同时修改该变量。 有序性:在单线程执行下,所有的代码操作计划都是顺序执行的,前面的代码总是优先执行。但是在多线程中,程序的执行顺序有可能就会出现混乱。 来源: oschina 链接: https://my.oschina.net/u/778766/blog/813196

原子性、可见性和有序性

会有一股神秘感。 提交于 2020-03-06 13:36:24
1. 原子性 操作是原子的,则它不可被分割。从另一个线程的视角来看,它不应该看到这个操作的中间状态,只能看到两种状态:①还没开始执行 ② 已经执行结束。 在多个线程访问临界资源时,如果临界区的代码不是原子的,则一个线程执行到某个中间状态,然后被时钟中断,另一个线程接着执行,就会覆盖数据或访问到不正确的数据。 2. 可见性 由于在存储器层次结构中,更高层的数据是更低层数据的一个子集,该数据副本可能存在存储系统的不同层中。为了提高性能,高层数据写回底层并不是实时的,因此可能造成在某时刻高层数据和底层数据的不一致。 在多核cpu下,由于每个核内部都内置了cache,各个核的cache可能缓存了同一个内存地址的数据,cache之间如果没有某种机制协调,会导致数据不一致(各个线程看到相同数据的值不一样)。 当一个线程修改了共享数据后,我们希望其他线程也能看到这个改变后的结果。如果其他线程看不到这个修改后的数据,继续访问缓存的旧数据,就可能出现问题。 3. 有序性 ① 哪些地方可能产生重排序? 源代码-->字节码/机器码-->存储系统-->CPU 会造成重排序的有:① 编译器 ② 存储系统 ③ CPU的执行单元。 ② 为什么要重排序呢? 为了提高性能。比如更利于cpu的流水、乱序执行;cpu的异步写入store buffer;store buffer的合并写操作等。 ③ 重排序会造成什么影响呢

【诡异并发三大恶人】原子性

余生颓废 提交于 2020-03-05 14:58:13
转:https://mp.weixin.qq.com/s/UiaXvnjc0NFfNMLf2lUq4A 前言、原子性的阐述 一个或者多个操作在 CPU 执行的过程中不被中断的特性称为原子性。 我理解是一个操作不可再分,即为 原子性 。而在并发编程的环境中,原子性的含义就是只要该线程开始执行这一系列操作,要么全部执行,要么全部未执行,不允许存在执行一半的情况。 我们试着从数据库事务和并发编程两个方面来进行对比: 1、在数据库中 原子性概念是这样子的:事务被当做一个不可分割的整体,包含在其中的操作要么全部执行,要么全部不执行。且事务在执行过程中如果发生错误,会被回滚到事务开始前的状态,就像这个事务没有执行一样。(也就是说:事务要么被执行,要么一个都没被执行) 2、在并发编程中 原子性概念是这样子的: 第一种理解:一个线程或进程在执行过程中,没有发生上下文切换。 上下文切换:指 CPU 从一个进程/线程切换到另外一个进程/线程(切换的前提就是获取 CPU 的使用权)。 第二种理解:我们把一个线程中的一个或多个操作(不可分割的整体),在 CPU 执行过程中不被中断的特性,称为原子性。(执行过程中,一旦发生中断,就会发生上下文切换) 从上文中对原子性的描述可以看出,并发编程和数据库两者之间的原子性概念有些相似: 都是强调,一个原子操作不能被打断! ! 而非原子操作用图片表示就是这样子的:

并发数据结构:迷人的原子

为君一笑 提交于 2020-03-05 01:47:21
随着多核CPU成为主流,并行程序设计亦成为研究领域的热门。 要想利用多核/多路CPU带来的强大功能,通常使用多线程来开发应用程序。但是要想拥有良好的硬件利用率,仅仅简单的在多个线程间分割工作是不够的。还必须确保线程大部分时间在工作,而不是在等待工作或等待锁定共享数据结构。 在不止一个线程访问共享数据时,所有线程都必须使用同步。如果线程间不进行协调,则没有任务可以真正并行,更糟糕的是这会给程序带来毁灭性的错误。 现在让我们来看一下在.NET和D语言中的标准同步手段-锁定。.NET下我们使用lock关键字,而D语言则使用 synchronized关键字。它们在Windows下均使用临界区(Critical Section)来实现,而在Linux下则使用互斥锁(Mutex)来实现。不论其如何实现,它们均强制实行互斥,来确保持有锁的线程对共享数据的独占访问权,以及当其他线程持有锁时,可以看到其对共享数据的修改。 简而言之,在基于锁的多线程编程中,任何针对共享数据,且有可能导致竞争条件的操作,我们都得将其改为原子操作(即连续的,不允许被打断的步骤;上面的lock/ synchronized 关键字就是我们实现原子操作的手段)。只要我们的线程持有锁,就不必担心其他线程会进来捣乱。 这听起来似乎很不错,我们只要加锁/解锁就可以为所欲为了。然而正是这种为所欲为的事实带来了问题

多线程编程学习十二(原子性、可见性与有序性)

半城伤御伤魂 提交于 2020-03-04 10:19:11
原子性 原子(atom)指化学反应不可再分的基本微粒,原子在化学反应中不可分割。原子操作指的是不可分割的整体,多线程的 原子性 指的是没有其他线程能够中断或检查正在原子操作中的变量。 从内存模型来看,直接保证的原子性变量操作包括 read、load、assign、use、store 和 write,我们大致可以认为基本数据类型的访问读写是具备原子性的。 从应用场景来看,JVM 保证原子性操作的主要有以下方式: synchronized 关键字。锁操作,基于 monitorenter 和 monitorexit 字节码指令,保证同步块只有单一线程执行。 AQS 锁机制。比如 ReentrantLock、ReentrantReadWriteLock 等,保证同步块只有单一线程执行。 CAS 实现。比如 java.util.concurrent.atomic 包中的诸多实现。 volatile 关键字。修饰变量,轻量锁机制,仅能保证对单个变量的操作具有原子性,复合操作不具备原子性。 可见性 可见性 是指当一个线程修改了共享变量的值,其他线程能够立即得知这个修改。 从应用场景来看,JVM 保证可见性主要有以下方式: volatile 关键字,它是如何保证可见性的呢? 当对 volatile 变量写的时候,会将当前处理器缓存行的数据写回到系统内存。 当对 volatile 变量读的时候

JAVA JUC

非 Y 不嫁゛ 提交于 2020-03-04 03:55:54
文章目录 JAVA JUC 一、Volatile 关键字-内存可见性 1、java线程的6种状态Thread.state: 2、JAVA多线程:判断 干活 改标志位加唤醒通知(详见Thread.start()方法) 3.一共两个线程 (1)同步锁:synchronized关键字,使两个线程同步 (2)Volatile关键字 (3)Volatile与Scnchronized比较 二、原子变量与CAS算法 1、i ++ 的原子性问题:i++ 的操作实际上分为三个步骤“读-改-写” 2、原子变量:JDK1.5以后 java.util.concurrent.atomic 包下提供了常用的原子变量 (1)原子变量特性 (2)原子性的解决过程:(先读取内存值,再进行比较,再进行赋值和写入) (3)原子变量的使用: 三、模拟CAS算法 四、ConcurrentHashMap锁分段机制 1、hashmap和hashtable区别: 2、ConCurrentHashMap采用“锁分段”机制 3、CopyOnWriteArrayList/CopyOnWriteArraySet : “写入并复制” 4、CountDownLatch(==闭锁==,在完成某些运算是,只有其他所有线程的运算全部完成,当前运算才继续执行) 四、创建执行线程的方式三:实现Callable接口 五、Lock同步锁 1

6.并发编程--volatile

不问归期 提交于 2020-03-03 13:47:05
并发编程--volatile volatile-说明 volatile关键字的作用是变量在多个线程可见; volatile 关键字是非原子性的 要是实现原子性操作,建议使用atomic类的系列对象:支持原子性操作(注意atomic类只保证本身方法的原子性,并不保证多次操作的原子性) 1. volatile : volatile关键字的作用是变量在多个线程可见; 示例: RunThread.java 说明: 在Java中,每个线程都会有一个工作内存区域,其中存放所有线程共享的主内存中的变量的值得拷贝。当线程执行的时候,在自己的工作内存区域中操作这些变量。为了存取一个共享的变量,一个线程通常先获得锁定并清除当前线程的内存工作区域,把这些共享变量从所有线程的共享内存区域中正确的装入到本身所以在的工作内存区域中,当线程解锁是保证该工作内存中的变量的值写会到共享内存区域中。 * 一个线程可以执行的操作有:使用(use),赋值(assgin),装载(load),存储(store),锁定(lock),解锁(unlock); * 主内存中可以执行的操作有:读(read),写(write),锁定(lock),解锁(unlock); 每个操作都是原子性的。 * volatile 的作用就是强制线程到主内存(共享内存)中去读取变量,而不是去线程工作内存区域里去读取,从而实现了多个线程间的变量可见

操作系统-原子性与锁机制

让人想犯罪 __ 提交于 2020-03-03 02:39:21
原子性和锁机制 所谓原子性和原子操作即一条或者一系列不可以被中断的指令 原子性的保证: 单核CPU如何保证指令的原子性? 单核CPU下各个指令都是串行的,中断只会发生在一条指令执行完毕,那么自然每个指令都是原子的,如果想要实现单核CPU下多个指令的的原子操作,则可以通过关中断实现 多核CPU如何保证指令的原子性? 多核CPU下由于出现并发问题,所以多个核心可能同时读写同一个内存,所以需要通过一些机制来实现原子操作 多核CPU确保一条指令的原子性,例如递减指令,实际上是三个操作,先读取内存,递减,写回,但是由于是多核CPU,所以即便是一条指令,也无法保证原子性,所以多核总线是通过总线锁来实现多核CPU的原子性的,即在指令执行前先通过总线锁锁住CPU和内存的通信(CPU1向总线发出LOCK#信号,其他处理器便不可以再访问该共享的内存) 总线锁会锁住CPU和内存的通信,开销大(此时并行化的操作变成了串行化的操作,但是),所以可以使用缓存锁,并通过缓存一致性机制来保证原子操作,缓存一致性即MESI协议,介绍如下: 每个CPU有自己的高速缓存,高速缓存以缓存行的形式存在,而MESI就是给每个缓存行保存一个标志位,标志位如下: M:被修改的,当前缓存行中的数据相对内存而言已经被修改了,但是还没有更新到内存中,同时处于该状态的数据只有当前CPU缓存中有,而其他CPU缓存中没有 E:独占的

【C/C++】实现数据结构广义表

末鹿安然 提交于 2020-03-01 07:40:01
1. 广义表的定义 每个元素可以为Atom,原子,也可以为线性表。 线性表的推广。线性表元素有唯一的前驱和后继,为线性表,而广义表是多层次的线性表 表头:第一个元素,可能是原子,可能是广义表 表尾:除了第一个元素,剩余的元素,所构成的广义表 举例: A = (a,b,(c,d),e) head(A) = a tail(A) = (b,(c,d),e) 遍历操作: 取表头,取表尾 ,取表头.. 长度:最外层的元素数,即最外层的','+1 深度:括号层数 2. 广义表的两种存储结构(子表法) 2.1链式存储结构 - 每个数据元素可以用一个节点表示 元素可以为原子或列表 原子节点:标志域、值域 列表节点:标志域、指示表头的指针域、指示表尾的指针域 空表:A = NULL 除了空表,表头指针指向一个表节点,表节点再分表头、表尾... 最高层表节点(可能原子、子表)的个数就是表长度? A = (a,b,(c,d),e) 最高层表头是先a,表尾是(b,(c,d),e),表头是b,表尾((c,d),e)..就是第一层的表尾直到为空之前,有过的表尾指针+1 判断是否在同一层次? 是这样的: 最高层处于同一层,后继的tail指针指向的是同一层,否则,head指针,或者表头是Atom,都是下一层。 2.2扩展线性表存储结构 不是用表头表尾指针了,而是,每一个节点,不管是子表还是原子