原子操作

java并发编程-12个原子类

大憨熊 提交于 2019-12-06 20:04:27
背景 多线程更新变量的值,可能得不到预期的值,当然增加syncronized关键字可以解决线程并发的问题。 这里提供另外一种解决问题的方案,即位于 java.util.concurrent.atomic包下的原子操作类,提供了一种用法简单,性能高效,线程安全的更新变量的方式。 其它两个附带的类顺带看了一下: LongAddr 多线程先的sum操作 LongAccomulator 多线程下的函数式操作,性能低于AtomicLong,主要是函数式的支持; 简单分类: 基本类型原子类 使用原子的方式更新基本类型,包括: AtomicBoolean AtomicInteger AtomicLong 核心方法: 直接看源码了。 类签名: public class AtomicInteger extends Number implements java.io.Serializable {} 方法 功能说明 构造方法 两个构造方法,不传或者传入值 get方法 get()获取值;对应的有set(int)方法,layzySet(int) 懒设置 getAndAdd(int) 获得老值然后增加一个数字, 对应的有addAndGet(int)增加一个数字并返回新值 getAndSet(int) 获得老值然后更新为新值 getAndIncreament() 获得老值然后+1

使用Lua脚本通过原子减防止超卖

纵然是瞬间 提交于 2019-12-06 08:28:43
需求   双十二要搞一个一分钱门票抢购的活动。 分析   性能分析,抢购时会发生高并发,如果仅仅依靠Mysql数据库,有可能因为大量的请求频繁访问数据库造成服务器雪崩,所以考虑通过Redis减库存,最终的数据落地到DB中。   在高并发的情况下,还要考虑到超卖的问题,因而打算使用Lua脚本完成原子减的操作。   在这里,我们只针对减库存的操作进行分析。 实现   不使用原子操作,出现超卖的情况。第一步:先从redis中查出库存进行判断,第二步:如果库存>0,则进行减库存的操作。   代码实现: 1 // 第一步:从redis中查出库存 2 Integer stock = (Integer) RedisUtils.get("stock"); 3 4 // 第二步:如果库存>0,则进行减库存的操作 5 if (stock > 0) { 6 long spareStock = RedisUtils.decr("stock", 1); 7 System.out.println(getName() + "抢到了第" + spareStock + "件"); 8 } else { 9 System.out.println("库存不足"); 10 }   用多线程模拟并发请求:库存为500,创建505个线程去抢购。 1 for(int i =1;i<=505;i++){ 2 MyThread2

第31课 std::atomic原子变量

馋奶兔 提交于 2019-12-06 05:41:19
一. std::atomic_flag和std::atomic (一)std::atomic_flag   1. std::atomic_flag 是一个bool类型的原子变量,它有两个状态set和clear,对应着flag为true和false。   2. std::atomic_flag使用前必须被 ATOMIC_FLAG_INIT 初始化,此时的flag为clear状态,相当于静态初始化。   3. 三个原子化操作   (1) test_and_set() :检查当前flag是否被设置。 若己设置直接返回true,若没设置则将flag置为true ,并返回false 。   (2) clear() ;清除flag标志,即flag=false。   (3)析构函数   4. 和所有atomic类型一样, std::atomic_flag不支持拷贝和赋值等操作 。因为赋值和拷贝调用了两个对象,从第一个对象中读值,然后再写入另一个。对于两个独立的对象,这里就有两个独立的操作,合并这两个操作必定不是原子的。   5. std::atomic_flag 类型不提供is_lock_free()。 该类型是一个简单的布尔标志, 并且 在这种类型上的操作都是无锁的 。但atomic_flag的可操作性不强,导致其应用局限性,还不如std::atomic<boo>。 (二)std:

宋宝华:关于ARM Linux原子操作的实现

寵の児 提交于 2019-12-06 04:47:45
本文系转载,著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。 作者: 宋宝华 来源: 微信公众号linux阅码场(id: linuxdev) 竞态无所不在 首先我们要理解竞态(race condition)无所不在,哪怕是对一个全局变量做++的加1动作。 a=0 a++; a++这句话,会被翻译为多条指令: ldr r3, [r3, #0] adds r2, r3, #1 str r2, [r3, #0] 它会先读(ldr),再修改(add),再写(str),是一个典型的读-修改-写(RMW)序列。a++在硬件上不是原子的! 假设2个线程(或者1个线程1个中断)“同时”做a++,因为加了2次,理论上a应该是等于2,但是结果a可能只是等于1,原因很简单: 假设第2个线程,在第一个线程做完读(LDR)之后,抢入率先做完a++,显然这个时候a=1,但是由于第一个线程在ldr指令里面已经读到了a=0,第1个线程在第2个线程做完a++后,继续做++还是会在0的基础上面加(只需要执行add和str指令了),所以导致第1个线程再++后,a还是等于1. 解决这样的race condition,我们需要把2个线程的a++的读-修改-写序列,串行化,彼此排他化。 也就是把这种交错的RMW: 变成这种先后发生的RMW: 这样第2个序列可以读到1,并且在1的基础上加1,保证结果是2。

聊聊并发(五)原子操作的实现原理

僤鯓⒐⒋嵵緔 提交于 2019-12-06 03:30:46
##引言## 原子(atom)本意是“不能被进一步分割的最小粒子”,而原子操作(atomic operation)意为”不可被中断的一个或一系列操作” 。在多处理器上实现原子操作就变得有点复杂。本文让我们一起来聊一聊在Inter处理器和Java里是如何实现原子操作的。 ##术语定义## 缓存行 :缓存的最小操作单位。 比较并交换 :CAS操作需要输入两个数值,一个旧值(期望操作前的值)和一个新值,在操作期间先比较下在旧值有没有发生变化,如果没有发生变化,才交换成新值,发生了变化则不交换。 CPU流水线 :CPU流水线的工作方式就象工业生产上的装配流水线,在CPU中由5~6个不同功能的电路单元组成一条指令处理流水线,然后将一条X86指令分成5~6步后再由这些电路单元分别执行,这样就能实现在一个CPU时钟周期完成一条指令,因此提高CPU的运算速度。 内存顺序冲突 :内存顺序冲突一般是由假共享引起,假共享是指多个CPU同时修改同一个缓存行的不同部分而引起其中一个CPU的操作无效,当出现这个内存顺序冲突时,CPU必须清空流水线。 ##处理器如何实现原子操作## 32位IA-32处理器使用基于对 缓存加锁 或 总线加锁 的方式来实现多处理器之间的原子操作。 ###处理器自动保证基本内存操作的原子性### 首先处理器会自动保证基本的内存操作的原子性

java如何实现原子操作CAS

蹲街弑〆低调 提交于 2019-12-06 03:30:34
在Java中可以通过锁和循环CAS的方式来实现原子操作。 使用循环CAS实现原子操作 JVM中的CAS操作正是利用了处理器提供的CMPXCHG指令实现的。自旋CAS实现的基本思路就是循环进行CAS操作直到成功为止。 CAS实现原子操作的三大问题 ABA问题,循环时间长开销大,以及只能保证一个共享变量的原子操作。 ABA问题 因为CAS需要在操作值的时候,检查值有没有发生变化,如果没有发生变化 则更新,但是如果一个值原来是A,变成了B,又变成了A,那么使用CAS进行检查时会发现它 的值没有发生变化,但是实际上却变化了。ABA问题的解决思路就是使用版本号。在变量前面 追加上版本号,每次变量更新的时候把版本号加1,那么A→B→A就会变成1A→2B→3A。从 Java 1.5开始,JDK的Atomic包里提供了一个类AtomicStampedReference来解决ABA问题。这个 类的compareAndSet方法的作用是首先检查当前引用是否等于预期引用,并且检查当前标志是 否等于预期标志,如果全部相等,则以原子方式将该引用和该标志的值设置为给定的更新值。 public boolean compareAndSet( V expectedReference, // 预期引用 V newReference, // 更新后的引用 int expectedStamp, // 预期标志 int

Java实现原子操作

不想你离开。 提交于 2019-12-06 03:30:23
Java实现原子操作 什么是原子操作 http://www.infoq.com/cn/articles/atomic-operations-and-contention 计算机操作最重要的构成单位是原子操作。这里的原子跟物理上说的原子没有任何关系,而是起源于单词atom,也就是希腊语“ἄτομος”(意为不可见的)。原子操作是一种不可再细分的操作,或者在系统中其他处理器看来是不可再分了。为了说明为什么原子操作很重要,考虑两个处理器以几乎相同的方式增加一个计数器,翻译成C语言就是counter++,此时会发生什么: 指令周期 处理器一 处理器二 0 reg = load(&counter); 1 reg = reg + 1; reg = load(&counter); 2 store(&counter, reg); reg = reg + 1; 3 store(&counter, reg); 在编译好的代码中, 这样一个操作分为:读操作、寄存器自加,最后是一个写操作(这里用类似C语言的伪代码表示)。 这三个步骤是独立且按顺序执行的(注意,对于x86来说,在更微观的架构层次上这句话是正确的,但是在指令集架构的层次上,这三步看起来可以用一条“读-修改-写(read-modify-write)”指令完成:add [memory], value)。并且因为这些操作被分成多个指令周期来执行

gcc原子操作测试

删除回忆录丶 提交于 2019-12-06 02:40:43
1 #include <stdio.h> 2 #include <pthread.h> 3 #include <stdlib.h> 4 5 static int count = 0; 6 7 void *test_func(void *arg) 8 { 9 int i=0; 10 for(i=0; i < 20000; i++) { 11 __sync_fetch_and_add(&count,1); 12 //count++; 13 } 14 return NULL; 15 } 16 17 int main(int argc, const char *argv[]) 18 { 19 pthread_t id[20]; 20 int i = 0; 21 22 for(i=0; i < 20; i++) { 23 pthread_create(&id[i],NULL,test_func,NULL); 24 } 25 26 for(i=0; i<20; ++i) { 27 pthread_join(id[i],NULL); 28 } 29 30 printf("%d\n",count); 31 return 0; 32 } 参考链接:https://blog.csdn.net/youfuchen/article/details/23179799 来源: https://www

Java并发编程系列:Java原子类

≯℡__Kan透↙ 提交于 2019-12-05 22:57:02
一、线程不安全 当多个线程访问统一资源时,如果没有做线程同步,可能造成线程不安全,导致数据出错。举例: @Slf4j public class ThreadUnsafe { // 用于计数的统计变量 private static int count = 0; // 线程数量 private static final int Thread_Count = 10; // 线程池 private static ExecutorService executorService = Executors.newCachedThreadPool(); // 初始化值和线程数一致 private static CountDownLatch downLatch = new CountDownLatch(Thread_Count); public static void main(String[] args) throws Exception{ for (int i = 0; i < Thread_Count; i++) { executorService.execute(() -> { for (int j = 0; j < 1000; j++) { // 每个线程执行1000次++操作 count++; } // 一个线程执行完 downLatch.countDown(); }); } //

Golang面向并发的内存模型

独自空忆成欢 提交于 2019-12-05 22:25:14
Import Advanced Go Programming 1.5 面向并发的内存模型 在早期,CPU都是以单核的形式顺序执行机器指令。Go语言的祖先C语言正是这种顺序编程语言的代表。顺序编程语言中的顺序是指:所有的指令都是以串行的方式执行,在相同的时刻有且仅有一个CPU在顺序执行程序的指令。 随着处理器技术的发展,单核时代以提升处理器频率来提高运行效率的方式遇到了瓶颈,目前各种主流的CPU频率基本被锁定在了3GHZ附近。单核CPU的发展的停滞,给多核CPU的发展带来了机遇。相应地,编程语言也开始逐步向并行化的方向发展。Go语言正是在多核和网络化的时代背景下诞生的原生支持并发的编程语言。 常见的并行编程有多种模型,主要有多线程、消息传递等。从理论上来看,多线程和基于消息的并发编程是等价的。由于多线程并发模型可以自然对应到多核的处理器,主流的操作系统因此也都提供了系统级的多线程支持,同时从概念上讲多线程似乎也更直观,因此多线程编程模型逐步被吸纳到主流的编程语言特性或语言扩展库中。而主流编程语言对基于消息的并发编程模型支持则相比较少,Erlang语言是支持基于消息传递并发编程模型的代表者,它的并发体之间不共享内存。Go语言是基于消息并发模型的集大成者,它将基于CSP模型的并发编程内置到了语言中,通过一个go关键字就可以轻易地启动一个Goroutine