可能重复:
Java: Why not to start a thread in the constructor? How to terminate?
我习惯在我的代码上运行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()
}
}
}
修改:
我最后将线程的开始移动到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();
}
...
答案 0 :(得分:5)
为什么在构造函数上创建新线程是不好的做法?
Findbugs提醒您注意对象构造周围的指令重新排序问题。虽然已分配新对象的内存空间,但无法保证在InnerThread
启动时已初始化任何字段。虽然final
字段将在构造函数完成之前初始化,但无法保证InnerThread
在启动时开始使用(例如)aField
,它将被初始化。 Java编译器出于性能原因执行此操作。它还可以选择在构造函数返回新实例后将非final字段的初始化移动到。
如果在构造函数中启动一个新线程,那么该线程可能会处理一个部分初始化的对象。即使thread.start()
是构造函数中的最后一个语句,由于重新排序,新线程可能正在访问部分构造的对象。这是Java语言规范的一部分。
以下是关于该主题的良好链接:calling thread.start() within its own constructor
它提到了以下内容:
通过在构造函数中启动它,可以保证违反Java Memory Model指南。有关详细信息,请参阅Brian Goetz's Safe Construction Techniques。
修改强>
由于您的代码正在启动正在访问afield
的新线程,因此根据Java Memory Model,无法保证在线程开始运行时afield
将被正确初始化。 / p>
我建议您在课程中添加start()
方法,调用thread.start()
。这是一种更好的做法,使得使用此类的其他类更容易看到在构造函数中创建线程。
答案 1 :(得分:2)
一般来说,最好对构造函数中的操作保持温和。
您的对象仍处于无效状态,因此您不希望任何人访问它。当你从构造函数启动一个线程时,它可能会引用正在构造的对象(否则为什么构造函数会启动它?)。此引用将指向线程启动时及其生效后不久的无效对象。那里有可怕的竞争条件。
以下是一篇关于它的好文章的链接http://www.ibm.com/developerworks/java/library/j-jtp0618/index.html
答案 2 :(得分:0)
每次实例化该类时都会创建一个线程。线程很昂贵,很难测试。如果实例化许多对象会遇到性能问题,则应考虑使用ThreadPool来修复线程数限制。此外,如果您在尝试对线程中发生的任何行为进行单元测试时遇到问题。