在对该关键字进行认识之前,需要对Java内存模型有一定的认识。
Java内存模型规定了所有的变量都存储在主内存中,每个线程都有自己的工作内存,线程的工作内存保存了该线程使用到的变量的是主内存副本的拷贝,线程对于变量的操作(读取,复制)都必须在工作内存中进行,而不能直接读写主内存中的变量。
在进行变量操作的时候,如果每次都是从主内存中读写,无疑是影响性能的,
即一个线程在操作某一个变量之前,会先把变量的值从主内存中拷贝一个副本放到工作内存中,操作完成之后同步到主内存中。不同的线程之间无法直接访问对方工作内存中的变量,线程间变量值的传递均需要通过主内存来完成。
volatile:
当一个变量被定义为volatile(不稳定的)之后,线程在进行变量操作的时候,都不会使用本地内存中之前缓存的数据,而是每次都会从主内存中把变量的值拷贝副本到工作内存中,这样就保证了变量对于所有线程的可见性。
public class TestVolatile {
public static void main(String[] args) throws Exception {
ThreadDemo td=new ThreadDemo();
Thread t=new Thread(td);
t.start();
while(true){
if(td.isFlag()){
System.out.println("OK");
break;
}
}
}
}
class ThreadDemo implements Runnable{
private volatile boolean flag=false;
@Override
public void run() {
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
flag=true;
System.out.println("flag="+isFlag());
}
public boolean isFlag() {
return flag;
}
public void setFlag(boolean flag) {
this.flag = flag;
}
}
需要特别注意的是,
即使volatile能够保证其变量对于线程的可见性,但是却不能保证并发的安全性。
比如多线程执行线程执行i++(i假设用volatilex修饰)也不能保证其安全性。
如下图:
因为i++并不是原子性的操作,当一个线程A再工作内存中进行数据操作的时候,还未结束的时候,cpu切换到了另一个线程B,而此时之前i++操作并没有结束,即并没有把数据刷新到主内存中,此时B获取到的数据跟A获取的数据实际上是一样的,这时候就存在了并发的问题了。