CAS 机制详解,android 开发基础知识点
相关文章:
=====
[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 的整数,
运行多次后可以看到,打印结果是小于 20000 的。为什么会出现这样的结果呢?
这是因为 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
L
2
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?
评论