您的位置:首页 > 编程语言 > Java开发

volatile与内存可见性

2017-02-14 17:16 239 查看
    在多线程环境下,对共享变量的操作,往往会遇到内存可见性问题。先看下面一段代码:

public class TestVoltatile {
public static void main(String[] args) {
ThreadDemo td = new ThreadDemo();
new Thread(td).start();

while (true) {
if (td.isFlag()) {
System.out.println("------------------");
break;
}
}
}
}

class ThreadDemo implements Runnable {
private boolean flag = false;

public void run() {

try {
Thread.sleep(200);
} 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;
}
}    这段你代码中,有两个线程,一个是main线程,一个是自己创建的线程(以下称为Thread-0)。其中Thread-0线程的run方法中去修改flag的值,main线程死循环判断flag的值如果为true则退出循环。运行上面的代码,发现Thread-0线程输出flag=true,但是程序却没有停止。
    这个问题涉及到了flag变量的内存可见性问题。每个线程在执行中都有一块缓存区,flag变量是类的属性,在堆中分配空间。当Thread-0线程要对flag的值进行修改的时候,会先到堆内存中读取flag的值到自己的线程缓存中,然后修改其值。修改之后,还没来得及将其写入到堆内存中,main线程在循环中判断isFlag()的时候就把堆中的flag值读到了自己的缓存中,此时为false。由于while(true)循环在jvm底层有做了优化,所以每次循环判断的falg值是main线程缓存中的,一直都是false。所以导致了flag值被修改后,main线程还是没有停止下来,一直在死循环。这就是内存可见性问题。

    解决这个问题我们可以使用同步,使用synchronized同步代码块将if包围起来,这样每次都能刷新缓存,取到最新的flag值。但是使用锁会导致性能的下降,多线程就会出现等锁的情况。

    还有一个解决方式就是使用volatile关键字修饰flag变量。被volatile修饰的变量,在每次使用时,都会到内存中去取最新的值,这就解决了多线程间内存可见性的问题,当多个线程进行操作共享数据时,可以保证内存中的数据可见。volatile不同于synchronized,volatile不具备”互斥性”,即多个线程可以同时访问,不需要抢锁。并且,volatile不能保证变量的"原子性"。

    解决方式一:

public static void main(String[] args) {
ThreadDemo td = new ThreadDemo();
new Thread(td).start();

while (true) {
synchronized (td) {
if (td.isFlag()) {
System.out.println("------------------");
break;
}
}
}
}
    解决方式二:

private volatile boolean flag = false;

内存可见性问题还可参考一下博客:
http://blog.csdn.net/beiyetengqing/article/details/49583381

http://www.cnblogs.com/frydsh/p/5720658.html

http://www.cnblogs.com/longshiyVip/p/5211476.html
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息