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

编程思想中关于线程启动的一个疑问
本帖最后由 qzhforthelife 于 2012-11-13 00:05:55 编辑

public class SelfManaged implements Runnable {
  private int countDown = 5;
  private Thread t = new Thread(this);
  public SelfManaged() { t.start(); }
  public String toString() {
    return Thread.currentThread().getName() +
      "(" + countDown + "), ";
  }
  public void run() {
    while(true) {
      System.out.print(this);
      if(--countDown == 0)
        return;
    }
  }
  public static void main(String[] args) {
    for(int i = 0; i < 5; i++)
      new SelfManaged();
  }
} /* Output:

java编程思想英文第4版,1137 页,第二自然段," but you should be aware that starting threads inside a constructor
can be quite problematic, because another task might start executing before the constructor
has completed, which means the task may be able to access the object in an unstable state",作者的意思是,不宜在构造里开启线程,因为有可能新的线程启动时,构造还未执行完,这时等于在用这个新线程来驱动一个不稳定的任务对象(毕竟构造未执行完,初始化任务还未做完)。
我的疑问在于:示例代码中,t.start()放在构造函数最后一行,这时只t.start()语句执行完,接着构造函数也执行完了(函数返回是原子操作,t.start()后面无其他语句,所以执行构造函数的线程不会中断),然后新的线程才会启动。不明白作者为何还说示例代码有问题,而且在后来的示例代码中,作者多次都在构造里启动线程(放在构造最后一行)。
总之我的意思是:只要保证在构造最后一行启动线程,那么在构造中启动线程就完全不会有任何问题,不是我的看法是否正确。求指点!能有反例代码最好!

------最佳解决方案--------------------
这跟类的构造是有关系的。比方说有个类:
pulbic class A{
    private String t="hello";
    private Integer p=4;
    ....................
}

那么当javac 去编译这个类时,会把这两个变量放到<init>方法里面去。构造函数的所有操作也将放大这个方法里面。也就是说你看到的启动线程是最后一句。经过编译器后,就不一定是最后一句了。而且现代的编译器都有编译优化,对调赋值语句的顺序也是有可能的。
当你在构造函数里面启动一个线程以后,当前对象的this引用就泄漏给这个新启动的线程了。当如上面所说的,这个对象很可能还没有构造完。比如A类中的两个成员变量还没有赋值,this引用就泄漏给了新启动的线程。那么通过this引用去获取 A类 中的 t对象将是空。而不是我们认为的"hello"。这是不安全的对象发布模式。所以不建议在构造函数中启动线程。
------其他解决方案--------------------
构造方法对应的<init>只要没有return,就表示还没有执行完,在调用start之后,CPU可能碰巧就立即就切换去执行新线程了,而构造方法还没有执行结束,对象也就还没有构建完整
------其他解决方案--------------------
引用:
构造方法对应的<init>只要没有return,就表示还没有执行完,在调用start之后,CPU可能碰巧就立即就切换去执行新线程了,而构造方法还没有执行结束,对象也就还没有构建完整

+1
------其他解决方案--------------------
反例代码:



public class SelfManaged implements Runnable {

  private int countDown = 5;
  private Thread t = new Thread(this);

  public SelfManaged() {
    t.start();
  }

  public String toString() {
    return Thread.currentThread().getName()
            + "(" + countDown + "), ";