在自己的构造函数中调用thread.start()

一个线程在它自己的构造函数中调用this.start()是否合法? 如果是这样,这会导致什么潜在问题? 我知道该对象在构造函数运行完成之前不会完全初始化,但除此之外还有其他问题吗?

出于内存安全原因,您不应将对象或该对象的字段的引用从其构造函数中公开给另一个线程。 假设您的自定义线程具有实例变量,通过从构造函数中启动它,您可以保证违反Java内存模型准则。 有关详细信息,请参阅Brian Goetz的安全施工技术 。

如果Thread类进一步被子类化,你也会看到奇怪的问题。 在这种情况下,一旦super()退出,你最终将运行该线程,并且子类可能在其构造函数中执行的任何操作都可能无效。

@bill barksdale如果线程已经在运行,再次调用start会得到一个IllegalThreadStateException,你不会得到2个线程。

我假设您要这样做以使您的代码更简洁; 而不是说

 Thread t = new CustomThread(); t.start(); activeThreads.add(t); 

你可以说

 activeThreads.add( new CustomThread() ); 

我也喜欢较少的冗长,但我同意其他受访者的意见,你不应该这样做。 具体来说,它打破了惯例; 任何熟悉Java的人都会读取第二个例子,他们会认为该线程尚未启动。 更糟糕的是,如果他们编写自己的线程代码以某种方式与您的交互,那么一些线程将需要调用start而其他线程则不需要。

当你独自工作时,这似乎并不引人注目,但最终你必须与其他人合作,并且开发良好的编码习惯是很好的,这样你就可以轻松地与他人合作并使用标准惯例。

但是,如果你不关心惯例并且讨厌额外的冗长,那就继续吧; 这不会导致任何问题,即使您尝试多次错误地start多次start

顺便说一句,如果一个人想要更低的冗长并仍然保持构造函数的“标准”语义,那么可以创建一个工厂方法:

 activeThreads.add( CustomThread.newStartedThread() ); 

这是合法的,但不明智。 实例的Thread部分将完全初始化,但您的构造函数可能不会。 没有理由扩展Thread,并且这样的技巧不会对你的代码有所帮助。

“合法的”,但我认为最重要的问题是:一个class级应该做一件事并且做得好。

如果您的类在内部使用了一个线程,那么该公共API中不应该显示该线程的存在。 这允许改进而不影响公共API。 解决方案:扩展Runnable,而不是Thread。

如果您的类提供了一般function,在这种情况下,恰好在一个线程中运行,那么您不希望自己限制为始终创建一个线程。 这里的解决方案相同:扩展Runnable,而不是Thread。

为了减少冗长,我建议使用工厂方法(例如Foo.createAndRunInThread())。

法律……是的(有其他地方提到的警告)。 建议……不。

我只是一种你很容易避免的气味。 如果你想让你的线程自动启动,就像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(); } } }