一个线程在它自己的构造函数中调用this.start()是否合法?如果是这样,这会导致什么样的潜在问题?我知道对象不会完全初始化,直到构造函数运行完成,但除此之外还有其他问题吗?
答案 0 :(得分:13)
出于内存安全原因,您不应将对象或该对象的字段的引用从其构造函数中公开给另一个线程。假设您的自定义线程具有实例变量,通过从构造函数中启动它,您可以保证违反Java内存模型准则。有关详细信息,请参阅Brian Goetz's Safe Construction Techniques。
答案 1 :(得分:3)
如果Thread类进一步被子类化,你也会看到奇怪的问题。在这种情况下,一旦super()退出,你最终会运行该线程,并且子类可能在其构造函数中执行的任何操作都可能无效。
@bill barksdale 如果线程已经在运行,再次调用start会导致IllegalThreadStateException,你不会获得2个线程。
答案 2 :(得分:2)
我认为你想这样做是为了让你的代码更简洁;而不是说
Thread t = new CustomThread();
t.start();
activeThreads.add(t);
你可以说
activeThreads.add( new CustomThread() );
我也喜欢不那么冗长,但我同意其他受访者的意见,你不应该这样做。具体来说,它打破了惯例;任何熟悉Java并且读取第二个示例的人都会认为该线程尚未启动。更糟糕的是,如果他们编写自己的线程代码以某种方式与您的交互,那么一些线程将需要调用start
而其他线程则不需要。
当你独自工作时,这似乎并不引人注目,但最终你必须与其他人合作,并且开发良好的编码习惯是好的,这样你就可以轻松地与他人合作并编写代码用标准惯例书写。
然而,如果你不关心惯例并且讨厌额外的冗长,那就继续吧;这不会导致任何问题,即使您尝试多次误拨start
。
答案 3 :(得分:2)
顺便说一下,如果一个人想要更低级的冗长并仍然保持构造函数的“标准”语义,那么就可以创建一个工厂方法:
activeThreads.add( CustomThread.newStartedThread() );
答案 4 :(得分:1)
这是合法的,但并不明智。实例的Thread部分将完全初始化,但您的构造函数可能不会。没有理由扩展Thread,并且这样的技巧不会对你的代码有所帮助。
答案 5 :(得分:1)
“合法”,但我认为最重要的问题是: 一堂课应该做一件事,做得好。
如果您的类在内部使用了一个线程,那么该公共API中不应该显示该线程的存在。这允许改进而不影响公共API。解决方案:扩展Runnable,而不是Thread。
如果您的类提供了一般功能,在这种情况下,恰好在一个线程中运行,那么您不希望自己限制为始终创建一个线程。这里的解决方案相同:扩展Runnable,而不是Thread。
为了减少冗长,我建议使用工厂方法(例如Foo.createAndRunInThread())。
答案 6 :(得分:1)
法律......是(如其他地方所述的警告)。建议......不。
我只是一种你很容易避免的气味。如果您希望自己的线程自动启动,请执行Heinz Kabutz。
public class ThreadCreationTest {
public static void main(String[] args) throws InterruptedException {
final AtomicInteger threads_created = new AtomicInteger(0);
while (true) {
final CountDownLatch latch = new CountDownLatch(1);
new Thread() {
{ start(); } // <--- Like this ... sweet and simple.
public void run() {
latch.countDown();
synchronized (this) {
System.out.println("threads created: " +
threads_created.incrementAndGet());
try {
wait();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
}
};
latch.await();
}
}
}