为什么在构造函数上创建新线程是不好的做法?

And*_*odi 2 java multithreading

可能重复:
Java:为什么不在构造函数中启动一个线程?如何终止?

我习惯在我的代码上运行FindBugs以查找错误或不良做法.今天它抱怨我正在类构造函数中启动一个线程.

真的是坏事吗?你能解释一下为什么吗?

如果我的班级是最终的,那至少是安全的吗?

编辑:

该线程是作为内部类实现的,它只使用在启动时已经初始化的主类的字段:

public final class SingletonOuter {
    private static SingletonOuter ourInstance = new SingletonOuter();

    public static SingletonOuter getInstance() {
        return ourInstance;
    }

    private final SomeOtherClass aField;

    private SingletonOuter() {
        aField=new SomeOtherClass(); 
        thread=new InnerThread();
        thread.start();
    }

    private boolean pleaseStop;

    private synchronized boolean askedStop(){return pleaseStop;}
    public synchronized void stop(){
        pleaseStop=true;  
    }

    private final InnerThread thread ;
    private class InnerThread extends Thread{
        @Override public void run() {
            //do stuff with aField until askedStop()
        }
    }

}
Run Code Online (Sandbox Code Playgroud)

编辑:

我最后将线程的开始移动到getInstance方法,以避免引入未来错误的可能性:

public final class SingletonOuter {
        private static SingletonOuter ourInstance

        public static SingletonOuter getInstance() {
            if (ourInstance==null){
                ourInstance= = new SingletonOuter();
                ourInstance.thread.start();
            }

            return ourInstance;
        }

        private final SomeOtherClass aField;

        private SingletonOuter() {
            aField=new SomeOtherClass(); 
            thread=new InnerThread();

        }
        ...
Run Code Online (Sandbox Code Playgroud)

Gra*_*ray 5

为什么在构造函数上创建新线程是不好的做法?

Findbugs通过指令重新排序对象构建的可能性来提醒您这个问题.虽然已分配新对象的内存空间,但无法保证InnerThread在启动时已初始化任何字段.虽然final字段在构造函数完成之前初始化,但无法保证在InnerThread启动时开始使用(例如)aField,它将被初始化.Java编译器出于性能原因执行此操作.它还可以选择在构造函数返回新实例之后将非final字段的初始化移动到.

如果在构造函数中启动新线程,则线程可能会处理部分初始化的对象.即使它thread.start()是构造函数中的最后一个语句,由于重新排序,新线程可能正在访问部分构造的对象.这是Java语言规范的一部分.

这是关于该主题的一个很好的链接:在自己的构造函数中调用thread.start()

它提到了以下内容:

通过从构造函数中启动它,您可以保证违反Java内存模型准则.有关详细信息,请参阅Brian Goetz的安全施工技术.

编辑:

由于您的代码正在启动正在访问的新线程afield,因此根据Java内存模型,无法保证afield在线程开始运行时将正确初始化.

我建议的是start()在你的类中添加一个调用的方法thread.start().这是一种更好的做法,使得使用此类的其他类更容易看到在构造函数中创建线程.