首先见下代码:
在下面的代码中,首先有一个多线程类,类中有共享数据flag,默认初始值为false;
程序启动后首先创建了一个thread子线程,在子线程中的逻辑是等待了200毫秒后将flag修改成了true,并打印flag的值
同时在主线程中进入while(true),判断flag的值是否为true,为true则打印flag的值并退出循环
public class VolatileDemo {
public static void main(String[] args) {
ThreadDemo threadDemo = new ThreadDemo();
new Thread(threadDemo).start();
while (true) {
if (threadDemo.isFlag()) {
System.out.println("main-flag=" + threadDemo.isFlag());
break;
}
}
}
}
class ThreadDemo implements Runnable {
private boolean flag = false;
@Override
public void run() {
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
flag = true;
System.out.println("flag=" + flag);
}
public boolean isFlag() {
return flag;
}
public void setFlag(boolean flag) {
this.flag = flag;
}
}
这段代码的最终输出结果为:并且jvm虚拟机一直未退出,因为程序一直在while循环中未结束。
flag=true
问题:
此时就会发现问题,明明在子线程中已经将flag修改成true了,但是在主线程中flag的值还是false,这就是内存可见性的问题
原因:
这个问题的原因是内存可见性的问题。
由于在程序运行的时候jvm会为每个线程都分配一个独立的内存用作缓存来提高效率。当程序启动的时候jvm就会为main主线程和ThreadDemo子线程分别分配独立的内存空间,且初始的时候flag在主内存中,且是false的状态;在子线程启动后,程序立即暂停了200毫秒,所以main主线程先执行了,主线程先从主存中获取了flag的值为false;然后子线程从主存中获取了flag的值然后修改了flag的值并且打印了flag的值,之后回写到了主存中。main主线程中while(true)的代码是较为底层的逻辑,执行效率非常高,导致一直无法从主存中获取到最新的flag的值,所以while中的if判断一直未false。
所以就出现了内存可见性的问题,简而言之,当线程操作共享数据时,各个线程的最新值彼此不可见。
解决方式1:
加锁:
while (true) {
synchronized (threadDemo) {
if (threadDemo.isFlag()) {
System.out.println("main-flag=" + threadDemo.isFlag());
break;
}
}
}
将while中的代码加上synchronized互斥锁,使用这个关键字来确定当前线程中的所以共享变量都是从主存中获取到的最新结果
缺点:由于互斥锁的关系,当一个线程在执行的时候,其他线程将会等待,就会出现效率低下的问题。
解决方式2:
使用volatile关键字
private volatile boolean flag = false;