Volatile实现变量可见性
先看一个例子
public class MyThread extends Thread{
private boolean isRunning=true;
public boolean isRunning() {
return isRunning;
}
public void setRunning(boolean isRunning) {
this.isRunning = isRunning;
}
@Override
public void run() {
System.out.println("进入了run");
while(isRunning){
}
System.out.println("结束");
}
public static void main(String[] args) {
try {
MyThread t = new MyThread();
t.start();
Thread.sleep(100);
t.setRunning(false);
System.out.println("isRunning设置成false");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
从上面运行结果中可以看出,虽然将isRunning变量设置成了false,但是该线程依然没有停止,还在继续运行。
这是由于java中有一块主内存,每个线程都有自己单独的工作内存,每个变量在主内存中有一个值。如果线程有使用到该变量,则该线程工作内存中会存在一个该变量的拷贝。当线程结束时会将线程工作内存中的变量拷贝同步回主内存。
出现打印结果现象的原因就是主内存和工作内存中数据的不同步造成的。因为执行run()方法的时候拿到一个主内存isRunning的拷贝,而设置isRunning是在main函数中做的,换句话说 ,设置的isRunning设置的是主内存中的isRunning,更新了主内存的isRunning,线程工作内存中的isRunning没有更新,当然一直死循环了,因为对于线程来说,它的isRunning依然是true。
解决这个问题很简单,给isRunning关键字加上volatile。加上了volatile的意思是,每次读取isRunning的值的时候,都先从主内存中把isRunning同步到线程的工作内存中,再当前时刻最新的isRunning。看一下给isRunning加了volatile关键字的运行效果:
private volatile boolean isRunning=true;
进入了run
isRunning设置成false
结束
volatile之前的普通线程内存模型
使用 volatile 之后
总结:(volatile 和 synchronized之间的不同)
1.volatile是线程同步的轻量实现,性能上比synchronized要好,volatile只能修饰变量,synchronized可以修饰方法,代码块。
2.多线程访问volatile不会阻塞,synchronized会发生阻塞
3.volatile可以保证可见性,但是不能保证原子性,而synchronized可以保证原子性,也可以间接保证可见性
4.volatile解决的是变量在多个线程中的可见性,而synchronized解决的是多个线程访问统一资源的同步性
volatile非原子特性
运行结果
证明了非原子性,但是保证是可见性的
下面更改代码 使用 synchronized 代替 volatile
运行结果
使用 synchronized 就没必要使用 volatile 了
synchronized拥有volatile的可见性
synchronized除了保障了原子性外,其实也保障了可见性。因为synchronized无论是同步的方法还是同步的代码块,都会先把主内存的数据拷贝到工作内存中(synchronized 只要一经线程调用,无所谓你锁的谁,只要跟进程相关的实例变量,静态变量都会同步到主内存),同步代码块结束,会把工作内存中的数据更新到主内存中,这样主内存中的数据一定是最新的。
可以看的出来 ·synchronized 只要一经线程调用,无所谓你锁的谁,只要跟进程相关的实例变量,静态变量都会同步到主内存,实现可见性