在自己的构造函数中调用thread.start()
线程在自己的构造函数中调用this.start()是否合法?如果是这样,这会导致哪些潜在问题?我知道在构造函数运行完成之前,对象不会完全初始化,但是除此之外,还有其他问题吗?
解决方案
回答
这是合法的,但不明智。实例的Thread部分将被完全初始化,但是构造函数可能没有初始化。扩展Thread的理由很少,而拉扯这样的技巧对代码也无济于事。
回答
我假设我们想这样做是为了使代码不那么冗长;而不是说
Thread t = new CustomThread(); t.start(); activeThreads.add(t);
你可以说
activeThreads.add( new CustomThread() );
我也喜欢减少冗长,但是我同意其他答复者的观点,即我们不应该这样做。具体来说,它违反了约定;阅读第二个示例的任何熟悉Java的人都将假定该线程尚未启动。更糟糕的是,如果他们编写自己的线程代码并以某种方式与线程交互,则某些线程将需要调用" start",而其他线程则不需要。
当我们独自工作时,这似乎并不引人注目,但是最终我们必须与其他人一起工作,养成良好的编码习惯是一件好事,这样我们就可以轻松地与他人一起工作,并使用标准约定。
但是,如果我们不关心约定并讨厌多余的冗长,请继续;否则,请继续。即使我们尝试错误地多次调用" start",这也不会造成任何问题。
回答
顺便说一句,如果希望较低的冗长性并且仍使构造函数保持其"标准"语义,则可以创建一种工厂方法:
activeThreads.add( CustomThread.newStartedThread() );
回答
这是"合法的",但我认为最重要的问题是:
一个班级应该做一件事并且做好。
如果类在内部使用线程,则该线程的存在在公共API中应该不可见。这样可以在不影响公共API的情况下进行改进。解决方案:扩展Runnable,而不是Thread。
如果类提供的一般功能(在这种情况下恰好在线程中运行),那么我们就不希望自己总是创建线程。此处的解决方案相同:扩展Runnable,而不是Thread。
为了减少冗长程度,我建议使用工厂方法(例如Foo.createAndRunInThread())。
回答
出于内存安全的原因,不应将对某个对象的引用或者该对象的字段从其构造函数中的另一个线程公开。假设自定义线程具有实例变量,则通过从构造函数内部启动它,可以确保我们违反了Java内存模型准则。有关更多信息,请参见Brian Goetz的《安全施工技术》。
回答
如果将Thread类进一步子类化,我们还将看到更多问题。在那种情况下,一旦super()退出,我们将最终使线程已经运行,并且子类在其构造函数中可能做的任何事情都是无效的。
@比尔·巴克斯代尔
如果线程已经在运行,则再次调用start会得到一个IllegalThreadStateException,而我们不会得到2个线程。