volatile是java 中的一个关键字,类似synchronized又有不同
volatile :一个变量被volatile 修饰之后,线程之间可以共享,就是所谓的线程共享变量
但为什么能够线程共享,又为什么volatile 不像synchronized能保证原子性,这要从java内存模型来说起。
Java内存模型定义了以下八种操作来完成:(这八种操作要对照这下面的图来理解)
- lock(锁定):作用于主内存的变量,把一个变量标识为一条线程独占状态。
- unlock(解锁):作用于主内存变量,把一个处于锁定状态的变量释放出来,释放后的变量才可以被其他线程锁定。
- read(读取):作用于主内存变量,把一个变量值从主内存传输到线程的工作内存中,以便随后的load动作使用
- load(载入):作用于工作内存的变量,它把read操作从主内存中得到的变量值放入工作内存的变量副本中。
- use(使用):作用于工作内存的变量,把工作内存中的一个变量值传递给执行引擎,每当虚拟机遇到一个需要使用变量的值的字节码指令时将会执行这个操作。
- assign(赋值):作用于工作内存的变量,它把一个从执行引擎接收到的值赋值给工作内存的变量,每当虚拟机遇到一个给变量赋值的字节码指令时执行这个操作。
- store(存储):作用于工作内存的变量,把工作内存中的一个变量的值传送到主内存中,以便随后的write的操作。
- write(写入):作用于主内存的变量,它把store操作从工作内存中一个变量的值传送到主内存的变量中。
1:普通变量在线程中的操作,简单分:1,2,3,4,5步
java 中,每个线程都是有各自的工作内存,而在进行操作的时候,都是从主内存读取到工作内存中来进行运算或者操作,并不影响主内存中的值,但是若是被volatile 修饰之后,说明是个共享变量,底层的汇编语言会加上汇编的lock前缀指令,并开启缓存一致性协议,a在变的时候就会刷新主内存中的a,但是为了保证刷新主内存中的a值之后被其他线程所知道,使用总线嗅探机制监听并通知其他线程,下面由两个线程来分析volatile 变量:
此图流程:2个线程同时完成 1234步,但是线程1是先完成第5步 ,这时候会触发缓存一致性,走678910,这时候线程2在做第5步的时候会重新读取a的值放入工作内存重新进行计算,其他情况最后说。
1:如果线程2的第5步在写失效之前,就是线程2已经把a=2赋值到工作内存了,这时候被置为失效, 会重新从主内存中读取数据,但不会在进行+1操作了,导致线程2的+1操作丢失,这也是为什么volatile 不能保证原子性,就是因为缓存一致性的写失效原因
2:如果两个线程同时完成第5步,谁会刷新到主内存?这时候CPU会随机选择一个,让另一个 线程的工作内存的共享变量a失效
PS:为什么要在写入前Lock ? 如果不Lock 的话,线程2得到写失效通知将a失效后,线程2重新读取主内存的值还没被线程1刷新。那样线程2加载到工作内存中的值还是老值,这样就违背共享了,Lock 之后,线程2读取的时候会读取不到,会等UnLock后读取,这样读取的就是新值
查了很多资料的个人理解,有不正之处还望指出改正。