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
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
相关文章推荐
- 内存可见性和原子性:Synchronized和Volatile的比较
- Java之多线程内存可见性_3(synchronized和volatile比较)
- 多线程之内存可见性Volatile(一)
- Java学习笔记--Volatile关键字和内存可见性
- 4000 内存可见性和原子性:Synchronized和Volatile的比较
- JUC01-volatile关键字和内存可见性
- volatile关键字如何保证内存可见性
- volatile关键字及内存可见性
- jvm(二)指令重排 & 内存屏障 & 可见性 & volatile & happen before
- java多线程之内存可见性-synchronized、volatile
- 【Java并发编程】之十五:并发编程中实现内存可见的两种方法比较:加锁和volatile变量
- Java并发:volatile内存可见性和指令重排
- Java多线程之内存可见性——volatile
- 原子性、内存可见性和重排序——重新认识synchronized和volatile
- 原子性,内存可见性和重排列-synchronized和volatile
- Java多线程之内存可见性——synchronized与volatile比较
- java多线程(1) ------volatile 和内存可见性
- Java 线程概述: 线程种类、状态,原子性、内存可见性、synchronized、volatile
- Java并发:volatile内存可见性和指令重排
- volatile之内存可见性