voliate特点:
JMM内存模型三大特性:可见性,原子性,有序性。
1.禁止指令重排序:
voliate实现禁止指令重排,避免多线程环境下程序出现乱序执行的现象。 多线程环境中线程交替执行,由于编译器优化重排的存在,两个线程中使用的变量能否保证一致性是无法确定的,结果无法预测。 (初始排序是123,最后排序可能为213,禁止重排后初始和最后排序一致) (重排:答题会做的先做,不会的后做,做题顺序和出题顺序不一致)
2.内存可见性:
线程首先将变量从主内存拷贝到自己的工作内空间,然后对变量进行操作,操作完再将变量写回主内存;第一时间通知其他线程,第一时间通知的机制就是Java(JMM内存模型)的可见性。
3.不保证原子性:
不能保证数据的完整性,会出现写覆盖,数据丢失。 解决原子性问题: 1.加synchronized 2.使用juc下面的AtomicInteger;Atomic的底层原理是CAS
AtomicInteger atomicInteger = new AtomicInteger();
public void addMyAtomic(){
atomicInteger.getAndIncrement();
}
单例的模式:双端检索机制;在需要单例的对象前加voliate(禁止指令重排) CAS(CompareAndSwap)底层原理:
比较并交换(比较当前工作内存中的值和主内存中的值是否一样,一样将内存值修改为更新值;如果不一样,重新获得主内存的值继续和工作内存中的值比较,直到一致为止)。 (getAndIncrement的底层调的是unsafe.getAndAddInt(this,valueOffset,1);this 当前对象;valueOffset:该对象的内存地址;1:需要变动的变量 这个对象的底层是CAS思想)
var1 AtomicInteger对象本身。 var2该对象值得引用地址。 var4需要变动的数量。 var5是用过var1 var2找出的主内存中真实的值。 用该对象当前的值与var5比较: 如果相同,更新var5+var4并且返回true, 如果不同,继续取值然后再比较,直到更新完成。
假设线程A和线程B两个线程同时执行getAndAddInt操作(分别跑在不同CPU上) : 1.AtomicInteger里 面的value原始值为5,即主内存中AtomicInteger的value为5,根据JMM模型,线程A和线程B各自持有一份值为5的value的副本分别到各自的工作内存。 2.线程A通过getIntVolatile(var1, var2)拿到value值5,这时线程A被挂起。 3.线程B也通过getIntVolatile(var1, var2)方法获取到value值5, 此时刚好线程B没有被挂起并执行compareAndSwapInt方法比较内存值也为5,成功修改内存值为6,线程B打完收工,一切0K。 4.这时线程A恢复,执行compareAndSwaplnt方法比较, 发现自己手里的值数字5和主内存的值数字6不-致,说明该值已经被其它线程抢先一步修改过了,那A线程本次修改失败,只能重新读取重新来一遍了。 5/线程A重新获取value值, 因为变量value被volatile修饰, 所以其它线程对它的修改,线程A总是能够看到,线程A继续执行compareAndSwaplnt进行比较替换,直到成功。
CAS的底层就是:Unsafe类+CAS思想(自旋) 内存值 V 旧的预期值 A 要修改的更新值 B 当且当 (内存值)V == A(预期值), V = B(将内存值修改为B)。
|