java中Atomic变量的实现原理是怎么样的( 二 )
内存值V旧的预期值A要修改的新值B当多个线程尝试使用CAS同时更新同一个变量时,只有其中一个线程能更新变量的值(A和内存值V相同时,将内存值V修改为B),而其它线程都失败,失败的线程并不会被挂起,而是被告知这次竞争中失败,并可以再次尝试(或者什么都不做)。
我们画张图来理解一下:
我们可以发现CAS有两种情况:
如果内存值V和我们的预期值A相等,则将内存值修改为B,操作成功!如果内存值V和我们的预期值A不相等,一般也有两种情况:重试(自旋)什么都不做我们再继续往下看,如果内存值V和我们的预期值A不相等时,应该什么时候重试,什么时候什么都不做。
1.2.1CAS失败重试(自旋)比如说,我上面用了100个线程,对count值进行加1。我们都知道:如果在线程安全的情况下,这个count值最终的结果一定是为100的。那就意味着:每个线程都会对这个count值实质地进行加1。
我继续画张图来说明一下CAS是如何重试(循环再试)的:
上面图只模拟出两个线程的情况,但足够说明问题了。
1.2.2CAS失败什么都不做上面是每个线程都要为count值加1,但我们也可以有这种情况:将count值设置为5
我也来画个图说明一下:
理解CAS的核心就是:CAS是原子性的,虽然你可能看到比较后再修改(compare and swap)觉得会有两个操作,但终究是原子性的!
二、原子变量类简单介绍原子变量类在java.util.concurrent.atomic包下,总体来看有这么多个:
我们可以对其进行分类:
基本类型:AtomicBoolean:布尔型AtomicInteger:整型AtomicLong:长整型数组:AtomicIntegerArray:数组里的整型AtomicLongArray:数组里的长整型AtomicReferenceArray:数组里的引用类型引用类型:AtomicReference:引用类型AtomicStampedReference:带有版本号的引用类型AtomicMarkableReference:带有标记位的引用类型对象的属性:AtomicIntegerFieldUpdater:对象的属性是整型AtomicLongFieldUpdater:对象的属性是长整型AtomicReferenceFieldUpdater:对象的属性是引用类型JDK8新增DoubleAccumulator、LongAccumulator、DoubleAdder、LongAdder是对AtomicLong等类的改进。比如LongAccumulator与LongAdder在高并发环境下比AtomicLong更高效。Atomic包里的类基本都是使用Unsafe实现的包装类。
Unsafe里边有几个我们喜欢的方法(CAS):// 第一和第二个参数代表对象的实例以及地址,第三个参数代表期望值,第四个参数代表更新值public final native boolean compareAndSwapObject(Object var1, long var2, Object var4, Object var5);public final native boolean compareAndSwapInt(Object var1, long var2, int var4, int var5);public final native boolean compareAndSwapLong(Object var1, long var2, long var4, long var6);从原理上概述就是:Atomic包的类的实现绝大调用Unsafe的方法,而Unsafe底层实际上是调用C代码,C代码调用汇编,最后生成出一条CPU指令cmpxchg,完成操作。这也就为啥CAS是原子性的,因为它是一条CPU指令,不会被打断。
推荐阅读
- dart这编程语言现在发展怎么样了,语法与Java,c#很相似,甚至更简洁
- Java工程师和C++工程师在工作上有啥区别哪个更适合自身发展
- 27岁,转行java的血与泪,该何去何从
- 怎样统计工程中未使用的java类
- 新互联网网站用Java还靠谱么对比Php,Python,Ruby的话
- 我想学java和安卓软件开发?
- 学计算机专业,java那些和网站开发选台式还是笔记本好
- JAVA设计思路
- 本人大专毕业一年,想要去培训,定了JAVAEE和安卓两个方向,应该学那个纠结,求帮助
- 从未接触过软件测试和java,可以学习主要是自学这两种其一吗
