Volatile关键字
先了解一个概念JMM内存模型图:
每个线程在操作的时候都会有一个属于自己的工作内存(即上图中的本地内存),这些工作内存存在的变量都是从主物理内存(即上图的主内存)拿到的,那么在进行更改的时候,需要在本地工作内存先进行操作,之后再写到主物理内存,其他线程再去取主物理内存中的值,确保数据在每个线程都是可见的,这个时候可能会有人有疑问为什么不直接在主物理内存操作,原因就是主物理内存并不能直接操作,不然也不会要有本地工作内存在,需要注意的是JMM模型并不真实存在,只是人们想象出来的一个模型
正文
你可以理解为这是一个轻量级的synchronized, volatile关键字可以保证可见性,有序性,需要注意的是volatile关键字并不能保证原子性,可以禁止指令重排,通常用于并发编程,何为原子性,就是某一个线程在进行某个业务操作时,不可分割,要么同时成功,要么同时失败,可以使用原子类(如:AtomicInteger)来解决volatile不具备原子性的问题。何为指令重排,java底层在进行编译代码的时候,会进行指令重排,计算机底层在执行代码的时有时候并不会按你写的代码顺序走,为了性能它可能会重新排序,但是一切都得遵循数据依赖性原则(就是要重新排序的东西得先确保定义了这个东西),详细见下:
具体代码示例会出现的情况:
CAS
全称Compare-And-Swap,是CPU的一条并发原语,具体功能判断内存某个位置的值是否被改过,如果是则更新新的值,整个过程是原子性的,底层是Unsafe类,CAS并发原与在Java中的体现就是Unsafe类下的各个方法,强调一下,由于CAS是系统一种系统原语,属于系统用语范畴,是由若干条命令组成的,用于完成某个功能的一个过程,并且原语的执行必须是连续的,在执行过程中不允许被中断,也就是说CAS是一条CPU的原子指令,不会造成所谓的数据不一致的问题。Unsafe下的所有方法都是native修饰的,也就是说直接操作底层操作系统资源执行响应任务的
应用:
CAS中有三个值,内存值V,旧的预期值A,要更改的值B
当且仅当A等于V时,将V修改为B,否则什么都不做,可以运用Atomic类来实现,如图所示:
CAS的方法其实是一个自旋锁,当条件成立的时候,他就跳出循环,没有成立期间,会一直进行自旋,如图所示:
人无完人,世间万物都是一个道理,即使说得CAS这么好,他也是有缺点的,就是想想,如果一直跳不出循环的话,会怎么样?对!会对CPU造成巨大的压力
那么说到这里,就不得不提到一个所谓的ABA问题了,何为ABA,听我慢慢跟你说
假设这个时候现在有两个线程,线程1和线程2(下面简称1,2),1是10秒钟执行一次线程,2是2秒钟执行一次线程,那么10秒的时间2是可以执行5次,显而易见,2比1速度快,那么此时2从主物理内存去拿值,值A,2从自己的工作内存将A修改为B,但是后来2脑子抽风了,又把B修改成了A,此时,1再去取主物理内存中的值,看起来是一样的,顺利拿到值A,但是实际上已经被2改过好几次了,1是毫不知情的,虽然结果没有变化,但是会带来什么后果呢?想想,一个小偷,把别人家的钱偷了之后又还了回来,还是原来的钱吗,你老婆出轨之后又回来,还是原来的老婆吗?ABA 问题也一样,如果不好好解决就会带来大量的问题。最常见的就是资金问题,也就是别人如果挪用了你的钱,在你发现之前又还了回来。但是别人却已经触犯了法律。如图所示:
那当然问题出现了,就会有结局问题的办法,如何解决ABA问题呢,在修改值的时候加上版本号即可: