1.2、什么是原子操作?
1.3、JDK中相关原子操作类
1.3.1、原子更新基本类型,
1.3.2、原子更新数组类型
1.3.3、原子更新引用类型
1.3.4、原子更新字段类
1.4、优化Demo2
1.5 详细解析CAS
相关文章:
=====
java高并发:CAS无锁原理及广泛应用(2w阅读量)
漫画:什么是 CAS 机制?(漫画版,深入浅出)
漫画:什么是CAS机制?(进阶篇)(Java语言CAS底层如何实现、什么是ABA问题?怎么解决?)
一、什么是CAS?
=========
具体可看 漫画:什么是 CAS 机制?
Atomic操作类的底层,正是利用了我们所说的CAS机制
CAS是英文单词Compare And Swap的缩写,翻译过来就是比较并替换。
CAS(V,E,N) 机制当中使用了3个基本操作数:内存地址V,旧的预期值A,要修改的新值B。
更新一个变量的时候,只有当变量的预期值A和内存地址V当中的实际值相同时,才会将内存地址V对应的值修改为B。
先来实现一段代码
1.1、Demo1
public class Demo1 {
int i = 0;
public void incr() {
i++;//加1操作
}
public static void main(String[] args)throws Exception {
Demo1 demo1 = new Demo1();
for (int j = 0; j < 2; j++) {//new 两个线程
new Thread(new Runnable() {
@Override
public void run() {
for (int k = 0; k < 10000; k++) {//每个线程执行10000遍
demo1.incr();
}
}
}).start();
}
Thread.sleep(1000);
System.out.println(demo1.i);//打印结果是否为20000?
}
}
打印结果:
可能是20000,更多的是小于20000的整数,
运行多次后可以看到,打印结果是小于200
《Android学习笔记总结+最新移动架构视频+大厂安卓面试真题+项目实战源码讲义》
【docs.qq.com/doc/DSkNLaERkbnFoS0ZF】 完整内容开源分享
00的。为什么会出现这样的结果呢?
这是因为 incr() 方法中的 i++ 操作不是原子性的,这就意味这它会被干扰。
在IDEA中,我们使用Show ByteCode(反编译字节码)看它的字节码文件:
再来看 incr() 方法中的 i++ 它实际上被分成了多步操作,因此不具备原子性
private incr()V
L0
LINENUMBER 7 L0
ALOAD 0
DUP
GETFIELD com/ph/cas/Demo1.i : I //获取字段值
ICONST_1
IADD //相加
PUTFIELD com/ph/cas/Demo1.i : I //赋值
L1
LINENUMBER 8 L1
RETURN
L2
LOCALVARIABLE this Lcom/ph/cas/Demo1; L0 L2 0
MAXSTACK = 3
MAXLOCALS = 1
1.2、什么是原子操作?
原子操作可以是一个步骤,也可以是多个步骤,但是其顺序不可以被打乱,也不可以被切割而只执行其中的一部分(不可中断性)。将整个操作视作一个整体是原子性的核心特征。
1.3、JDK中相关原子操作类
1.3.1、原子更新基本类型,
Atomic包提供了以下3个类。·AtomicBoolean AtomicInteger AtomicLong。
1.3.2、原子更新数组类型
AtomicIntegerArray:原子更新整型数组里的元素。
AtomicLongArray:原子更新长整型数组里的元素。
·AtomicReferenceArray:原子更新引用类型数组里的元素。
1.3.3、原子更新引用类型
AtomicReference:原子更新引用类型。
AtomicMarkableReference,AtomicStampeReference
1.3.4、原子更新字段类
AtomicIntegerFieldUpdater:原子更新整型的字段的更新器。
AtomicIntegerFiledUpdater,AtomicLongFiledUpdater
下面我们对Demo1进行优化:
1.4、优化Demo2
public class Demo2 {
AtomicInteger i = new AtomicInteger(0);
private void incr(){
i.incrementAndGet();//对变量加1操作,并返回(原子操作)
}
public static void main(String[] args)throws Exception {
Demo2 demo2 = new Demo2();
for (int j = 0; j < 2; j++) {//new 两个线程
new Thread(new Runnable() {
@Override
public void run() {
for (int k = 0; k < 10000; k++) {//每个线程执行10000遍
demo2.incr();
}
}
}).start();
}
Thread.sleep(1000);
System.out.println(demo2.i);//打印结果是否为20000?
}
}
打印结果:20000
我们来看 inrc() 的字节码文件:
private incr()V
L0
LINENUMBER 10 L0
ALOAD 0
GETFIELD com/ph/test/Demo2.i : Ljava/util/concurrent/atomic/AtomicInteger;
INVOKEVIRTUAL java/util/concurrent/atomic/AtomicInteger.incrementAndGet ()I
POP
接下来进行深入研究: