对于被voildate修饰过的变量大部分人都知道当一个线程修改过后对另外一个线程可见,具体是为什么资料的讲解比较少,通过读并发编程的艺术对voildate变量的原理机制做下总结。
对于voildate变量来讲是有两个语义的,一个是可见性问题,另外一个是禁止指令重排。
1、可见性问题
相对于内存,CPU的速度是极高的,如果CPU需要存取数据时都直接与内存打交道,在存取过程中,CPU将一直空闲,这是一种极大的浪费,所以,现代的CPU里都有很多寄存器,多级cache,他们比内存的存取速度高多了。某个线程执行时,内存中的一份数据,会存在于该线程的工作存储中(working memory,是cache和寄存器的一个抽象,每个线程都有自己的工作存储),并在某个特定时候回写到内存。单线程时,这没有问题,如果是多线程要同时访问同一个变量呢?内存中一个变量会存在于多个工作存储中,线程1修改了变量a的值什么时候对线程2可见?此外,编译器或运行时为了效率可以在允许的时候对指令进行重排序,重排序后的执行顺序就与代码不一致了,这样线程2读取某个变量的时候线程1可能还没有进行写入操作呢,虽然代码顺序上写操作是在前面的。这就是可见性问题的由来。
使用voildate变量,当写的时候,JMM会把该线程对应的本地内存中的共享变量值刷新到主内存中去,如果读时,JMM会把该线程对应的本地内存置为无效,会直接从内存中去读。
2、禁止重排序
加上voildate之后,voildate之后的变量不会排到前面去。
不管是可见性问题还是禁止指令重排,底层都是通过加lock的方式来构建的一个内存屏障来做的