为何下面这段程序不能结束?
class T implements Runnable {
boolean f = true; public void run() {
while (f) {
}
}
public void stopRunning() {
f = false;
}
}public class TestThread {
public static void main(String[] args) throws Exception {
T t = new T();
Thread th = new Thread(t);
th.start();
for (int i = 0; i < 5; i++) {
System.out.println("Alive:" + th.isAlive());
Thread.sleep(10); //这行注释掉程序可以结束
}
t.stopRunning();
}
}

解决方案 »

  1.   

    我的eclipse4.2里程序不能正常结束。线程th不知道为什么不能结束。
      

  2.   

    boolean volatile f = true;
    试试
    这个问题好像是跟内存模型有关,
    我是看到书上说会有这种问题,
    不过我自己没实验出来
      

  3.   

    跟java多线程的内存模型有关具体可以参见Effective Java 第二版的多线程部分由于变量 f 没有同步,
    public void run() {
      while (f) {
      }
    }
    这段代码在某些jvm上可以被合法优化为:
    public void run() {
      if (f) {
        while(true) {
        }
      }
    }
      

  4.   


    Effective Java 讲到了源生类型(primitive type)的同步问题
    即 int, char, boolean ... 等的同步问题有时人们说,源生类型除了 long 和 double 以外,其他都是“天生线程安全”的,不需要做特别的同步,Effective Java里指出了这种说法的不实之处。根据Java的规范,除了 long 和 double 之外,其他所有的源生类型的读操作和写操作都有原子性保护,
    就是说,在一个多线程的环境下,一个 int 变量即使被不同的线程读/写,
    也保证不会出现某个线程读到一个“刚被写了一个字节,其他三个字节还是原来旧的二进制值”的混乱值(arbitrary value)但是“线程安全”包含两个方面:1 - 对所有的线程,都以“稳定的状态”呈现
    2 - 所有的线程都能及时看到最新的更改上面说的源生类型的 读 或 写 的天生原子性,保证了第一点,没有保证第二点,及一个源生类型变量的值在一个线程中被改变,这个改变不能保证被另一个线程及时看到,甚至不能保证另一个线程最终能不能看到。因为有这样的特性,所以4楼提到的那种运行时优化是合法的,在某些较早的jvm上也是确实存在的。源生类型已经满足了第一点,要再满足第二点,加 volatile 关键字即可。另外,引用(reference)在这个问题上,与源生类型非常相似,Java 1.5以后,reference的同步可以用 volatile 来支持。要注意的是 volatile 后的源生类型变量只是满足了 单个操作 的线程安全,向下面这样的:volatile int a = 1;void increase() {
      a++;
    }
    实际包含了读和写两个操作,volatile 不能为其提供原子性保护,这就是为什么会有 AtomicInteger 等类,这些类为源生类型提供一些原子性的读写方法。
      

  5.   

    楼主所要了解的是 并发可见性 问题以及 Java内存模型相关知识http://www.ticmy.com/?p=5http://www.ticmy.com/?p=5
      

  6.   


    前面几位说的都挺好,尤其是推荐你去看看ticmy的Blog关于:“Thread.sleep(10); //这行注释掉程序可以结束”
    这其实是你误会了线程启动执行的真实时机问题,并不是你调用了start(),线程就立即开始run()了。
    也就说:可能在你子线程还没启动完毕,你的for循环就已经结束,那么就会触发将stopRunning(),之后子线程才正式开始执行,而此时f已经是false。所以你误以为是程序可以正常结束,但其实是因为 while(f) 刚想开始第一次循环,f 就已经是false 了,所以根本没有循环成。所以关键问题是确保子线程已经开始执行再调用stopRunning(),而非sleep()自身的原因。
      

  7.   

    谢谢楼上几位。
    start(),run()的关系我是了解的。这个程序的问题是在一台机器上运行不能结束,明显是因为子线程不能结束而导致main线程不能结束。
    Thread.sleep(10); 的目的是保证大部分执行情况下子线程能在main线程执行for循环时能执行。
    再次感谢各位!
      

  8.   


    说法不准确。main()函数结束后,main线程即结束,并启动Destory线程。但子线程不结束,只是会阻止整个程序结束,并不会阻止main线程结束。另外,如果子线程是守护线程,则不能阻止程序结束。