java 多线程中的一个奇怪的地方,希望有识之人帮助解答疑惑

码上中国博客 发布于 02/14 09:25
阅读 267
收藏 0

简单一个例子如下:

public class VolatileTest {
	public static void main(String[] args) throws InterruptedException {
		MyThread1 myThread1 = new MyThread1();
		myThread1.start();
		Thread.sleep(3000);
		myThread1.setFlag(false);
		Thread.sleep(1000);
		System.out.println("flag:"+myThread1.flag);
		System.out.println("结束");
	}
}
class MyThread1 extends Thread{
	
	public boolean flag = true;
	
	public void setFlag(boolean flag) {
		this.flag = flag;
	}
	
	@Override
	public void run() {
		System.out.println("线程开始>>>>>>>>>>>>>");
		while (flag) {
//			System.out.println("运行中,子线程:"+Thread.currentThread().getName());
		}
		System.out.println("线程结束");
	}
	
}

在实际运行中,可以尝试分别注释和不注释下面的代码

System.out.println("运行中,子线程:"+Thread.currentThread().getName());

甘肃11选5_[官网首页]会产生不同的效果。

例如,不注释的话,线程会打印上面的日志,直到整个线程终结,eclipse的控制台的运行标志也会变灰(这是重点)

但是如果注释掉上面这行代码,则eclipse的控制台运行标志会一直为运行状态红色(运行中)

初学多线程,请有识之士帮助解决下这个原理是什么。

加载中
0
tcxu
tcxu

楼主的案例有二。
   案例1:注释掉这行代码,即便驱动方法 main 线程也终止,线程 Thread-0 仍然保持运行, 所以 eclipse 的控制台运行标志为红色,表示还有程序正在运行。

   案例2:不注释的话,即 由于 线程 Thread-0 的 while 循环体的代码调用 System.out.println(...) 打印出信息,而终止了该线程。之后,驱动方法 main 线程也终止,致使 eclipse的控制台的运行标志显示变灰: 无程序运行。

   案例1的解释:
"Java内存模型分为主内存,和工作内存。甘肃11选5_[官网首页]主内存是所有的线程所共享的,工作内存是每个线程自己有一个,不是共享的"。下图表明线程、主内存、工作内存三者之间的交互关系:


每个线程都有自己的工作内存,工作内存中保存了被该线程使用到的变量的主内存副本。线程对变量的所有操作(读取、赋值),都是在工作内存中进行,而不是直接读写主内存中的变量,即谈不上内存间的"可见性"。线程之间,无法直接访问对方工作内存中的变量,而线程间变量值的传递均需通过主内存来完成。
甘肃11选5_[官网首页]因此,线程 Thread-0 的变量 flag 在其工作内存的值始终为 true。而主方法 main 线程,执行 myThread1.setFlag(false) 操作,只是将主内存里的 flag 变为 false,而 Thread-0 线程的工作内存里的变量 flag,仍然保持为 true 不变。故 Thread-0 线程将永远运行,致使 eclipse 的控制台运行标志永远显示红色。 

   案例2的解释:
此时,while 循环体内,添加了 System.out.println(...)操作。由于 println 内部代码(源码)的同步 synchronized 关键字,导致 CPU 的输出比较耗时。这时 CPU 就有可能、有时间去保证内存的可见性,即统一了主内存和工作内存的变量 flag 的 false 值,于是 while 循环就被终止,即 线程 Thread-0 终止。

     若将 类 MyThread1 的公有属性(成员变量 flag)冠以关键字 volatile, 就能强制地保证线程内存的可见性。这样,注释 还是不注释  System.out.println(...),就没有区别了:最终都会终止 线程 Thread-0 的运行,控制台终显灰色。

参考:

  1. 多线程:为什么在while循环中加入System.out.println,线程可以停止
  2. Java---线程多(工作内存)和内存模型(主内存)分析
  3. Java线程工作内存与主内存变量交换过程及volatile关键字理解
0
却又让幽兰枯萎
却又让幽兰枯萎

 

private boolean getFlag(){

    return flag;

}

public void run() {

            System.out.println("线程开始>>>>>>>>>>>>>");

            while (getFlag()) {

                    // 这里休眠一下也可以:Thread.sleep(100);

                    // System.out.println("运行中,子线程:"+Thread.currentThread().getName());

            }                

         System.out.println("线程结束");

}

却又让幽兰枯萎
却又让幽兰枯萎
这个问题和编译器有关,要真正理解问题的原因需要一些汇编的知识
返回顶部
顶部

页面底部区域 foot.htm