多线程 - 关于Java内存可见性的问题
问题描述
请看以下代码
public class TestVolatile {public static void main(String[] args) throws InterruptedException {ThreadDemo td = new ThreadDemo();new Thread(td).start();Thread.sleep(1);while(true){ if(td.isFlag()){System.out.println('------------------');break; }} }}class ThreadDemo implements Runnable { private boolean flag = false; @Override public void run() {try { Thread.sleep(200);} catch (InterruptedException e) {}flag = true;System.out.println('flag=' + isFlag()); } public boolean isFlag() {return flag; } public void setFlag(boolean flag) {this.flag = flag; }}
把Thread.sleep(1)换成Thread.sleep(1000)就能获取flag修改后的值,即td.isFlag()返回true。虽然看了Java内存模型的概念,但我不知道如何解释这段代码,谁能解释一下?
相关问题: Java多线程的工作内存是什么?
问题解答
回答1:你得先说说你的预期效果是啥?问问题要问清楚啊
回答2:这个期待是没有规范支撑的。代码中没有做任何能保证 '子线程写 happen-before 主线程读' 的事情。
sleep(1000)后看到修改只是巧合,一个JVM如果在更久后才让主线程看到,甚至永远不让主线程看到都不违反规范。
回答3:你的程序应该是想测试 volatile 关键字的功能。但是 “把 Thread.sleep(1) 换成 Thread.sleep(1000) 就能获得预期效果” 这样做理解上是不对的。首先,程序中总共有两个线程,主线程(暂称 线程M)和 new Thread(td) (暂称 线程T)。
当写 Thread.sleep(1) 的时候,线程M 在 1ms 之后,便开始在 while(true) 循环中检查 td.isFlag() 的值,但是因为内存可见性的关系,线程M 并不能及时读取 线程T 中 flag 的值,所以此时导致了死循环;
当写 Thread.sleep(1000) 的时候,M 在 1000ms 之后,开始在 while(true) 循环中检查 td.isFlag() 的值;但是 T 在 200ms 的时候,便将 flag 的值设为 true 了,所以,M 在 1000ms 之后检测 td.isFlag() 的值肯定是返回 true 的,那么第一次判断便会返回 true,产生输出并跳出 while(true) 循环。
为了让 线程M 及时读取到 线程T 中 flag 的值,需要将 flag 使用 volatile 关键字进行修饰:
private volatile boolean flag = false;
那么每次对 flag 的修改,其他线程都立马可见。关于 volatile 的使用,可以参考我的博客:Java 多线程(6):volatile 关键字的使用
回答4:可以参考如下三个代码:其中第一个和你的情况一样,由于多线程的可见性问题,可能导致无限循环下去。第二个是使用synchronized解决此问题,大多数工作场景用这个好第三个是使用volatile解决,但这个关键字只保证可见性,在实际场景中局限性比较大,得慎用
public class StopThread {private static boolean stopRequested;public static void main(String[] args) throws InterruptedException {Thread backgroundThread = new Thread(new Runnable() {@Override public void run() {@SuppressWarnings('unused')int i = 0;while(!stopRequested) {// System.out.println('加上这一句程序就可以终止,否则无限循环下去'); i++;} }});backgroundThread.start();TimeUnit.SECONDS.sleep(1);stopRequested = true; }}
public class StopThread2 {private static boolean stopRequested;public static synchronized boolean getStopRequested() {return stopRequested; }public static synchronized void requestStop() {stopRequested = true; }public static void main(String[] args) throws InterruptedException {Thread backgroundThread = new Thread(new Runnable() {@Override public void run() {@SuppressWarnings('unused')int i = 0;while(!getStopRequested()/* stopRequested */) { i++;} }});backgroundThread.start();TimeUnit.SECONDS.sleep(1);requestStop();/* stopRequested = true; */ }}
public class StopThread3 {private static volatile boolean stopRequested; public static void main(String[] args) throws InterruptedException {Thread backgroundThread = new Thread(new Runnable() {@Override public void run() {@SuppressWarnings('unused')int i = 0;while(stopRequested) { i++;} }});backgroundThread.start();TimeUnit.SECONDS.sleep(1);stopRequested = true; }}
相关文章:
1. PHP中的$this代表当前的类还是方法?2. javascript - vue2.0中使用vue2-dropzone的demo,vue2-dropzone的github网址是什么??百度不到。3. 鼠标过导航标签时,无效果,请问如何查找问题4. javascript - avalon.js ms-on 事件绑定无效 ?5. html - 这种错位的时间轴怎么布局,然后用css实现?6. html5 - 目前 公司App 嵌入H5页面 做个 手机支付功能 没有做过 所以 请求各位有经验的 给个思路7. java - spring-data Jpa 不需要执行save 语句,Set字段就可以自动执行保存的方法?求解8. IOS app应用软件的id号怎么查询?比如百度贴吧的app-id=4779278139. java - butterknife怎么绑定多个view10. java - 如图代码,Collection 类中的iterator()是抽象方法,为什么可以调用?