volatile 变量修饰的共享变量进行写操作前会在汇编代码前增加 lock 前缀:
1),将当前处理器缓存行的数据写回到系统内存;
2),这个写会内存的操作会使其它 cpu 缓存该内存地址的数据无效。
Java 语言 volatile 关键字可以用一句贴切的话来描述 “ 人皆用之,莫见其形 “。理解 volatile 对理解它对理解 Java 的整个多线程的机制非常有帮助。
JVM 内存结构中有一个非常重要的内存区域叫做线程栈 , 每个线程的栈大小可以通过设置 JVM 参数-Xss, -Xss128k 表示每个线程堆栈大小为 128K,JDK1.5 默认值为 1M。
线程栈内存存储了基本类型变量和对象引用,当访问了对象的某一实例变量时,通过在栈中获得对象引用再获取变量的值,然后将变量的值拷贝至线程的工作内存。
每个线程 (处理器) 都有工作内存,工作内存存了该线程以读写共享变量的副本。工作内存是 JMM 抽象概念 , 并不真实存在。
它涵盖了缓存、写缓冲区、寄存器和其它硬件和编译器优化。
1),read and load 从主存复制变量到当前工作内存;
2),use and assign 执行代码,改变共享变量值;
3),store and write 用工作内存数据刷新主存相关内容;
4),其中 use and assign 可以多次出现。
但是这一些操作并不是原子性,也就是在 read load 之后,如果主内存 count 变量发生修改之后,线程工作内存中的值由于已经加载,不会产生对应的变化,所以计算出来的结果会和预期不一样。
可见性指的是一个线程对变量的写操作对其他线程后续的读操作可见。
由于现代 CPU 都有多级缓存,CPU 的操作都是基于高速缓存的,而线程通信是基于内存的,这中间有一个 Gap, 可见性的关键还是在对变量的写操作之后能够在某个时间点显示地写回到主内存,这样其他线程就能从主内存中看到最新的写的值。volatile,synchronized(隐式锁), 显式锁,原子变量这些同步手段都可以保证可见性。可见性底层的实现是通过加内存屏障实现的:
1),写变量后加写屏障,保证 CPU 写缓冲区的值强制刷新回主内存;
2),读变量之前加读屏障,使缓存失效,从而强制从主内存读取变量最新值。
总结: 在并发环境保证有序性、可见性,但原子性没办法保证。