volatile是java的一个关键字,他可以修饰变量,对于java并发来讲,他有不可替代的作用。
一概念的解释
原子性:在提出这个概念的人当时认为,原子是最小的单位,不可分割,所以他的意思是,在一个原子范围内的操作要不都成功,要不都失败,不可分割。
JMM:java 内存模型,java 内存模型是,一个进程内所有线程公用主内存,线程内部有私有内存,当操作时,线程会去主内存读取公共变量,拷贝副本到私有内存,当处理完成之后就会把私有内存的值写回主内存。其实线程私有内存是一个抽象概念,内部是通过缓冲区,寄存器等完成的。
可见性:指的是当线程把本地内存的值写回主内存是,会让其他线程知道。至于怎么让其他线程知道,这就不是这个概念的事。
指令重排序:java编写的.java 文件计算机是"不认识的",计算认识的只有机器码,一般理解是汇编代码。但是,javac编译器会把我们.java 编译成jvm认识的class文件,也就是中间文件。然后jvm会把class翻译成汇编语言,但我们编写的代码对于计算机来讲,他的执行效率是不高的,所以会对他进行优化,这就会造成语句(或者说成指令更准确)顺序发生变化。这也就是指令从排序。
二volatile的作用
- volatile能够保证变量的可见性,但不能保证原子性。volatile进行写操作时,处理机会发送一条#Lock指令,这条指令的作用是
- 将当前处理机的缓存行数据写回内存。
- 使CPU里缓存了改地址的数据无效。
所以他是有效的。但他仅仅保证了一个变量,不能保证原子性,当然他对于多线程编程来讲是很有用的。
- volatile 能够防止指令重排序,volatile的内存语义是:当写一个volatile变量时,jmm会把线程对应的工作内存的共享变量写入主内存,当读一个volatile变量时,jmm会把线程工作内存对于的共享变量置为无效,然后线程回去主内存中读取该共享变量。
三volatile内存语义的实现
在讲volatile的内存语义实现之前,先将四条内存屏障,storestore storeload loadload loadstore。
s1 storestore s2 : s1的刷新会内存先于s2及后面所有的刷新会内存
s1 storeload s2 : s1的刷新会内存先于s2及后面所有的装载
s1 loadload s2 : s1的装载先于s2及后面所有的装载
s1 loadstore s2: s1的装载先于s2及其后面所有的刷新会内存。
对于volatile写,会在前面插入一条,storestore 语句,这能够保证volatile写之前,普通写都写好,然后在volatile写之后加一条storeload,这样能够保证读取的是最新的数据。对于volatile读,首先会在volatile读后面加一条loadload内存屏障,这样保证volatile读和普通读不进重排序,然后在loadload 后面加一条loadstore,这样避免了volatile读和普通写重排序。
当然对于连续使用volatile操作,处理机也会进行优化。比如,volatile读,loadload ,volatile读,loadstore,普通写,
storestore,volatile写,sstorestore ,volatile写,storeload。