And*_*odi 2 java multithreading
我习惯在我的代码上运行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)
为什么在构造函数上创建新线程是不好的做法?
Findbugs通过指令重新排序对象构建的可能性来提醒您这个问题.虽然已分配新对象的内存空间,但无法保证InnerThread
在启动时已初始化任何字段.虽然final
字段将在构造函数完成之前初始化,但无法保证在InnerThread
启动时开始使用(例如)aField
,它将被初始化.Java编译器出于性能原因执行此操作.它还可以选择在构造函数返回新实例之后将非final字段的初始化移动到.
如果在构造函数中启动新线程,则线程可能会处理部分初始化的对象.即使它thread.start()
是构造函数中的最后一个语句,由于重新排序,新线程可能正在访问部分构造的对象.这是Java语言规范的一部分.
这是关于该主题的一个很好的链接:在自己的构造函数中调用thread.start()
它提到了以下内容:
通过从构造函数中启动它,您可以保证违反Java内存模型准则.有关详细信息,请参阅Brian Goetz的安全施工技术.
编辑:
由于您的代码正在启动正在访问的新线程afield
,因此根据Java内存模型,无法保证afield
在线程开始运行时将正确初始化.
我建议的是start()
在你的类中添加一个调用的方法thread.start()
.这是一种更好的做法,使得使用此类的其他类更容易看到在构造函数中创建线程.
归档时间: |
|
查看次数: |
3156 次 |
最近记录: |