原子操作

代码面试之广义表

社会主义新天地 提交于 2020-03-01 06:57:57
广义表的基本概念 广义表(Lists,又称列表) 是线性表的推广。线性表定义为n>=0个元素a1,a2,a3,…,an的有限序列。线性表的元素仅限于原子项,原子是作为结构上不可分割的成分,它可以是一个数或一个结构,若放松对表元素的这种限制,容许它们具有其自身结构,这样就产生了广义表的概念。 广义表是n (n>=0)个元素a1,a2,a3,…,an的有限序列,其中ai或者是原子项,或者是一个广义表。通常记作LS=(a1,a2,a3,…,an)。LS是广义表的名字,n为它的长度。若ai是广义表,则称它为LS的子表。 抽象数据类型广义表的定义如下: ADT Glist { 数据对象: D={ei | i=1,2,..,n;n>=0 ; eiÎAtomSet 或ei ÎGlist, AtomSet为某个数据对象} 数据关系:R1={< ei-1, ei > | ei-1 , ei ÎD,2<=i<=n} 基本操作: InitGList( &L); 操作结果:创建空的广义表L。 CreateGList(&L,S); 初始条件:S是广义表的书写形式串。 操作结果:由S创建广义表L。 DestroyGList(&L); 初始条件:广义表L存在。 操作结果:销毁广义表L。 CopyGList( &T,L); 初始条件:广义表L存在。 操作结果:由广义表L复制得到广义表T。 GListLength

Go

血红的双手。 提交于 2020-02-28 07:54:39
1. Go中的原子操作 原子性: 一个或多个操作在CPU的执行过程中不被中断的特性,称为原子性。这些操作对外表现成一个不可分割的整体,他们要么都执行,要么都不执行,外界不会看到他们只执行到一半的状态。 原子操作: 进行过程中不能被中断的操作,原子操作由底层硬件支持,而锁则是由操作系统提供的API实现,若实现相同的功能,前者通常会更有效率 最小案例: package main import ( "sync" "fmt" ) var count int func add (wg *sync.WaitGroup) { defer wg.Done() count++ } func main () { wg := sync.WaitGroup{} wg.Add( 1000 ) for i := 0 ; i < 1000 ; i++ { go add(&wg) } wg.Wait() fmt.Println(count) } count 不会等于1000,因为 count++ 这一步实际是三个操作: 从内存读取 count CPU更新 count = count + 1 写入 count 到内存 因此就会出现多个goroutine读取到相同的数值,然后更新同样的数值到内存,导致最终结果比预期少 2. Go中sync/atomic包 Go语言提供的原子操作都是非入侵式的,由标准库中 sync

redis操作库存 再同步到数据库里

。_饼干妹妹 提交于 2020-02-27 15:14:37
redis单线程的,线程安全,在redis里操作库存,完了之后把库存同步到数据库里,mq同步。 redis 非阻塞的,单线程也很快。 否则,在这个记录基础上 多线程修改,不安全 如,在100基础上,两个线程一块改-1,都变为99,应该是98才对。 这个就跟原子性一样,i-- 不是原子的。修改记录(update)也是 。 redis原子性 https://blog.csdn.net/ahjxhy2010/article/details/80520434 来源: CSDN 作者: cainiao1923 链接: https://blog.csdn.net/cainiao1923/article/details/104531068

【Java并发工具类】原子类

泄露秘密 提交于 2020-02-27 12:55:23
前言 为保证计数器中 count=+1 的原子性,我们在前面使用的都是 synchronized 互斥锁方案,加锁独占访问的方式未免太过霸道,于是我们来介绍另一种解决原子性问题的 无锁方案:原子变量 。在正式介绍原子变量之前,我们先来总结下锁的不足,然后深入介绍原子变量。 锁的劣势 通过对共享变量加锁,使得获取到锁的线程可以采用独占方式来访问共享变量,并且对变量的修改对随后获取这个锁的其他线程都是可见的(Happens-Before规则)。 当多个线程同时请求锁时,对于没有获取到锁的线程将有可能被挂起并且在稍后才恢复运行(有时会选择自旋等待)。当线程恢复执行时,必须等待其他线程执行完它们的时间片后,才能被调度执行。我们需要知道, 在挂起和恢复线程等过程中会存在着很大的开销,并且通常存在着较长时间的中断 。 加锁方案在竞争激烈的情况下,其性能会由于上下文切换的开销和调度延迟而降低。而如果锁的持有时间非常短,那么在不恰当的时间请求锁时,线程休眠的代价将会不太划算。 加锁还存在一些缺点: 当一个线程正在等待锁时,它不能做任何其他事情。 如果一个线程在持有锁的情况下被延迟执行(例如发生了缺页错误、调度延迟、或者其他情况),那么所有需要这个锁的线程都无法执行下去。 如果被阻塞的线程优先级较高,而持有锁的线程优先级较低,那么将会导致优先级反转问题。 即使高优先级的线程可以抢先执行

AtomicReference与volatile的区别

穿精又带淫゛_ 提交于 2020-02-27 08:53:29
首先volatile是java中关键字用于修饰变量,AtomicReference是并发包java.util.concurrent.atomic下的类。 首先volatile作用,当一个变量被定义为volatile之后,看做“程度较轻的 synchronized”,具备两个特性: 1.保证此变量对所有线程的可见性(当一条线程修改这个变量值时,新值其他线程立即得知) 2.禁止指令重新排序 注意volatile修饰变量不能保证在并发条件下是线程安全的,因为java里面的运算并非原子操作。 volatile说明 java.util.concurrent.atomic工具包,支持在单个变量上解除锁的线程安全编程。其基本的特性就是在多线程环境下,当有多个线程同时执行这些类的实例包含的方法时,具有排他性,即当某个线程进入方法,执行其中的指令时,不会被其他线程打断,而别的线程就像自旋锁一样,一直等到该方法执行完成,才由JVM从等待队列中选择一个另一个线程进入,这只是一种逻辑上的理解。 AtomicReference说明 Java 理论与实践: 正确使用 Volatile 变量 Java 语言中的 volatile 变量可以被看作是一种 “程度较轻的 synchronized ”;与 synchronized 块相比,volatile 变量所需的编码较少,并且运行时开销也较少

2、原子性、可见性与有序性

别来无恙 提交于 2020-02-26 05:23:08
原子性(Atomicity) :由Java内存模型来直接保证的原子性变量操作包括read、load、assign、use、store和write,我们大致可以认为 基本类型的访问读写是具备原子性的。 如果应用场景还需要一个更大范围的原子性保证,Java内存模型还提供了lock和unlock操作来满足这种需求。 可见性(Visibility) :可见性是指当一个线程修改了共享变量的值,其他线程能够立即得知这个修改。Java内存模型是通过在变量修改后将 新值同步到主内存,无论是普通变量还是volatile变量都是如此,普通变量与volatile变量的区别是,volatile的特殊规则保证了新值能立即同步到主内存,以及每次使用前立即从主内存刷新。因此,可以说volatile保证了多线程操作时变量的可见性,而普通变量不能保证这一点。 有序性(Ordering) :Java程序天然的有序性可以总结为一句话:如果本线程内观察,所有的操作都是有序的;如果在一个线程中观察另一个线程,所有的操作都是无序的。前半句是指“线程内表现为串行的语义”,后半句是指“指令重排序”现象和“工作内存与主内存同步延迟”现象。 来源: https://www.cnblogs.com/feijishuo/p/4546274.html

🔥史上最全的Java并发系列之Java并发机制的底层实现原理

社会主义新天地 提交于 2020-02-26 05:10:55
并发编程的3个基本概念 原子性 定义: 即一个操作或者多个操作 要么全部执行并且执行的过程不会被任何因素打断,要么就都不执行。 原子性是拒绝多线程操作的,不论是多核还是单核,具有原子性的量,同一时刻只能有一个线程来对它进行操作。简而言之,在整个操作过程中不会被线程调度器中断的操作,都可认为是原子性。例如 a=1是原子性操作,但是a++和a +=1就不是原子性操作。Java中的原子性操作包括: a. 基本类型的读取和赋值操作,且赋值必须是数字赋值给变量,变量之间的相互赋值不是原子性操作。 b.所有引用reference的赋值操作 c.java.concurrent.Atomic.* 包中所有类的一切操作 可见性 定义:指当多个线程访问同一个变量时,一个线程修改了这个变量的值,其他线程能够立即看得到修改的值。 在多线程环境下,一个线程对共享变量的操作对其他线程是不可见的。Java提供了volatile来保证可见性,当一个变量被volatile修饰后,表示着线程本地内存无效,当一个线程修改共享变量后他会立即被更新到主内存中,其他线程读取共享变量时,会直接从主内存中读取。当然,synchronize和Lock都可以保证可见性。synchronized和Lock能保证同一时刻只有一个线程获取锁然后执行同步代码,并且在释放锁之前会将对变量的修改刷新到主存当中。因此可以保证可见性。 有序性 定义

并发编程三要素:原子性,有序性,可见性

跟風遠走 提交于 2020-02-24 10:46:50
并发编程三要素 原子性: 一个不可再被分割的颗粒。原子性指的是一个或多个操作要么全部执行成功要么全部执行失败。 有序性: 程序执行的顺序按照代码的先后顺序执行。(处理器可能会对指令进行重排序) 可见性: 一个县城对共享变量的修改,另一个线程能够立刻看到。 一、原子性 线程切换会带来原子性的问题 int i = 1; // 原子操作 i++; // 非原子操作,从主内存读取 i 到线程工作内存,进行 +1,再把 i 写到主内存。 虽然读取和写入都是原子操作,但合起来就不属于原子操作,我们又叫这种为“复合操作”。 我们可以用synchronized 或 Lock 来把这个复合操作“变成”原子操作。 例子: //使用synchronized private synchronized void increase(){ i++; } //使用Lock private int i = 0; Lock mLock = new ReentrantLock(); private void increase() { mLock.lock(); try { i++; } finally{ mLock.unlock(); } } 这样我们就可以把这个一个方法看做一个整体,一个不可分割的整体。 除此之前,我们还可以用java.util.concurrent.atomic里的原子变量类

可见性、原子性和有序性问题

时光怂恿深爱的人放手 提交于 2020-02-24 10:45:34
可见性、原子性和有序性问题 并发编程背景 核心矛盾 这些年,我们的 CPU、内存、I/O 设备都在不断迭代,不断朝着更快的方向努力。但是,在这个快速发展的过程中,有一个 核心矛盾一直存在,就是这三者的速度差异 。 我形象的描述了一下这三者的速度上的差异:所谓天上一天地上一年(爱因斯坦的相对论是有合理解释的),CPU和内存之间的速度差异就是CPU天上一天,内存地上一年(假设 CPU 执行一条普通指令需要一天,那么 CPU 读写内存得等待一年的时间)。内存和I/O设备的速度差异就更大了,内存是天上一天,I/O设备是地上十年。 木桶原理 一只水桶能装多少水取决于它最短的那块木块 程序的大部分语句都要访问内存,有些还要访问I/O,根据木桶原理,程序整体的性能取决于最慢的操作,所以只是单方面的提高CPU的性能是无效的,才出现了一些提高CPU性能使用率的优化 如何提高CPU的性能? CPU增加了缓存,以均衡与内存的速度差异(计算机体系结构方面优化) 操作系统增加了进程、线程,以分时复用CPU,进而均衡CPU与I/O设备的速度差异(操作系统方面优化) 编译程序优化指令执行次序,使得缓存能够得到更加合理地利用(编译程序带来的优化) 很多时候人们总是会很开心享受眼前的好处,却总是忽略了一句话:采用一项技术的同时,一定会产生一个新的问题,并发程序很多的诡异问题的根源也从这里开始 可见性 单核时期

Java并发编程(三)概念介绍

狂风中的少年 提交于 2020-02-23 16:28:26
在构建稳健的并发程序时,必须正确使用线程和锁。但是这终归只是一些机制。要编写线程安全的代码,其核心在于要对状态访问操作进行管理,特别是对 共享的(Shared) 和 可变的(Mutable) 状态的访问。 对象的状态是指存储在 状态 变量(例如实例或静态域)中的数据。 对象的状态可能包括其他 依赖对象的域 。比如某个HashMap的状态不仅是HashMap对象本身,还存储在许多Map.Entry对象中。 "共享"意味着变量可以由多个线程同时访问,而"可变"则意味着变量的值在其生命周期内可以变化。 线程安全性在于如何防止数据上发生 不可控的并发访问 。 一个对象是否 需要是线程安全的 ,取决于它是否被多个线程访问。 要使对象是线程安全的,需要采用 同步机制 来协同对对象可变状态的访问。如果无法实现协同,那么可能导致数据破坏以及其他不该出现的结果。 协同多个线程对变量访问的同步机制主要有: 1. 关键字 synchronized 2. 关键字 volatile 3. 显式锁 (Explicit Lock) 4. 原子变量 协同多线程访问一个可变的状态变量的方法有: 1. 不在线程之间共享该状态变量 2. 将状态变量修改为不可变的变量 3. 在访问状态变量时使用同步 什么是线程安全? 一个类在多线程环境下被访问,这个类始终能表现出正确的行为,那么就称这个类是线程安全的。