日期:2014-05-20  浏览次数:20806 次

高分求解多线程问题!
为何下面这段程序不能结束?
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();
}
}

------最佳解决方案--------------------

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 等类,这些类为源生类型提供一些原子性的读写方法。




------其他解决方案--------------------
跟java多线程的内存模型有关

具体可以参见Effective Java 第二版的多线程部分

由于变量 f 没有同步,


public void run() {
  while (f) {
  }
}


这段代码在某些jvm上可以被合法优化为:


public void run() {
  if (f) {
    while(true) {
    }
  }
}

------其他解决方案--------------------
楼主所要了解的是 并发可见性 问题以及 Java内存模型相关知识

http://www.ticmy.com/?p=5

http://www.ticmy.com/?p=5
------其他解决方案--------------------
boolean volatile f = true;
试试
这个问题好像是跟内存模型有关,
我是看到书上说会有这种问题,
不过我自己没实验出来
------其他解决方案--------------------
引用:
为何下面这段程序不能结束?


前面几位说的都挺好,尤其是推荐你去看看ticmy的Blog

关于:“Thread.sleep(10); //这行注释掉程序可以结束”
这其实是你误会了线程启动执行的真实时机问题,并不是你调用了start(),线程就立即开始run()了。
也就说:可能在你子线程还没启动完毕,你的for循环就已经结束,那么就会触发将stopRunning(),之后子线程才正式开始执行,而此时f已经是false。

所以你误以为是程序可以正常结束,但其实是因为 while(f) 刚想开始第一次循环,f 就已经是false 了,所以根本没有循环成。

所以关键问题是确保子线程已经开始执行再调用stopRunning(),而非sleep()自身的原因。

------其他解决方案--------------------
不知道你的是什么问题,但是在我的环境上一切正常。