关于java:在其自己的构造函数中调用thread.start()

关于java:在其自己的构造函数中调用thread.start()

calling thread.start() within its own constructor

本问题已经有最佳答案,请猛点这里访问。

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


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


如果将Thread类进一步子类化,您还将看到更多问题。在这种情况下,一旦super()退出,您将最终使线程已经运行,并且子类在其构造函数中可能做的任何事情都是无效的。

@比尔·巴克斯代尔
如果线程已经在运行,则再次调用start将获得IllegalThreadStateException,而您不会获得2个线程。


顺便说一句,如果希望较低的冗长性并仍使构造函数保持其"标准"语义,则可以创建一种工厂方法:

1
activeThreads.add( CustomThread.newStartedThread() );

我假设您想这样做是为了使您的代码不那么冗长;而不是说

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

你可以说

1
activeThreads.add( new CustomThread() );

我也喜欢减少冗长,但是我同意其他答复者的观点,即您不应该这样做。具体来说,它违反了约定;熟悉Java的任何人如果阅读第二个示例,都将假定该线程尚未启动。更糟糕的是,如果他们编写自己的线程代码并以某种方式与您的线程交互,则某些线程将需要调用start,而其他线程则不需要。

当您独自工作时,这似乎并不引人注目,但是最终您必须与其他人一起工作,并且养成良好的编码习惯是一件好事,这样您就可以轻松地与他人一起工作,并用标准约定。

但是,如果您不关心约定并讨厌多余的冗长内容,请继续进行;即使您尝试错误地多次调用start,这也不会造成任何问题。


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

我只是一种气味,您只能轻易避免。如果您希望线程自动启动,请像Heinz Kabutz一样进行。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
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();
    }
  }
}

这是"合法的",但我认为最重要的问题是:
一个班级应该做一件事并且做好。

如果您的类在内部使用线程,则该线程的存在在公共API中应该不可见。这样可以在不影响公共API的情况下进行改进。解决方案:扩展Runnable,而不是Thread。

如果您的类提供的一般功能(在这种情况下恰好在线程中运行),那么您就不希望自己总是创建线程。此处的解决方案相同:扩展Runnable,而不是Thread。

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


这是合法的,但不明智。实例的Thread部分将被完全初始化,但是您的构造函数可能不会初始化。扩展Thread的理由很少,而拉扯这样的技巧对您的代码也无济于事。


推荐阅读