volatile关键字
volatile关键字可以对类的成员变量与静态变量进行修饰
volatile关键字的作用
1.保证被修饰属性的可见性,被修饰后的属性如果被更改后其他线程是会立即可见的
2.保证被修饰属性的有序性,被修饰后的属性禁止修改指令执行的顺序
注意:volatile关键字不能保证属性的原子性
底层实现原理
有序性:(内存屏障指令)被volatile修饰后,会对变量施加一个内存屏障指令,禁止指令的重排序来确保指令的有序性
可见性:(Lock前缀指令+缓存一致性协议MESI)被volatile修饰后,会对变量添加Lock前缀指令,在属性被修改之后会对CPU发出一条带有Lock前缀的指令, 触发各个CPU嗅探,嗅探发展主线的值进行修改后会将自己缓存区中的值过期并重新赋值
解决原子性
原子性的解决有两条方案
1.对修改共享变量的代码加锁,是阻塞式的实现
2.使用原子类提供的原子变量,非阻塞式的实现
java中提供了JUC包用于提供并发编程中的原子类
原子类的原子性是由volatile+CAS机制实现的
CAS机制
CAS的全称是:Compare-And-Swap 比较并且交换,是应硬件层面上的支持
CAS是一种乐观锁的实现,是基于自旋锁思想的一种轻量级的锁适合用于并发量比较小情况
CAS中的三个操作数
V:内存值,指主内存中共享变量的值
A:预估值
B:更新值
原理:
在CPU读取共享变量时,会在工作内存中将读取的变量值存储一份并作为预估值
在CPU进行对变量的操作之后,会将其存储为更新值
对主内存的值进行更新操作时,会先使用预估值与内存值是否一致,如果一致则可以进行操作,如果不一致则重写读取主内存的值重新执行一遍算法
优点
不用添加锁是非阻塞式的实现,能够提高程序的执行效率
提高CPU的利用率
不加锁,所有的线程都可以进行访问
缺点
自旋锁会不断的循环判断,容易大量占用CPU的资源
只适合并发量较小情况,如果请求量较大CPU承载能力有限
ABA问题
ABA问题指的是如果在一个线程对共享变量的执行期间,有两个线程对的共享变量做出了修改,并且最后修改的值与两个线程修改前的值是一样的
例如:共享变量a="A"
线程1读取a="A"
线程2修改a="B"
线程3修改a="A"
此时线程1就无法判断共享变量的值是否被修改过了
解决方法:对共享变量添加版本号,每次操作共享变量时进行版本号的比较即可