【问题标题】:Java starting a thread pool in objects constructorJava 在对象构造函数中启动线程池
【发布时间】:2010-08-16 16:50:20
【问题描述】:

在对象构造函数中启动线程池是否安全?我知道你不应该从构造函数启动线程,关于“this”指针转义的事情(我不完全理解这一点,但会做更多的搜索来尝试弄清楚)。

代码如下所示:

private ExecutorService pool;

public handler()
{
  pool = Executors.newCachedThreadPool();
}

public void queueInstructionSet(InstructionSet set)
{
  pool.submit(new Runnable that handles this instruction set);
}

如果这不起作用,我可以将此类创建为 Runnable 并在新线程中启动它。但是,这似乎会给程序添加一个不必要的线程,而它实际上并不需要它。

谢谢。

编辑:

感谢大家的回复,他们肯定有助于理解这一点。

根据代码,在我看来,这个构造函数创建线程池是有道理的,但让我解释一下这段代码具体在做什么,因为我可能正在以一种奇怪的方式思考这个问题。

这个对象的全部意义在于获取“指令集”对象,并相应地对它们进行操作。指令集来自连接到服务器的客户端。一旦从客户端接收到完整的指令集,就会将该指令集发送到此对象(处理程序)进行处理。

此处理程序对象包含对指令集可以操作的每个对象的引用。它将指令集提交给线程池,线程池会找到该指令集要与哪个对象进行交互,然后处理该对象上的指令集。

我可以处理 IO 服务器中的指令集对象,但我的想法是有一个单独的类,因为它使整个代码更具可读性,因为每个类只专注于做一件特定的事情。

想法?建议?

谢谢

【问题讨论】:

    标签: java threadpool


    【解决方案1】:

    您的示例代码根本不会让“this”逃脱。在构造函数中启动一个新线程是相当安全的(甚至使用this 作为Runnable,在这个例子中你没有这样做)只要你确定你已经初始化了对象因为新线程将需要它。例如,设置一个新线程启动线程后将依赖的 final 字段将是一个非常糟糕的主意:)

    基本上让“this”引用转义通常很讨厌,但并非普遍如此。在某些情况下它是安全的。小心点。

    话虽如此,让构造函数启动线程可能会被视为在构造函数中做的太多。很难说它在这种情况下是否合适 - 我们对您的代码在做什么知之甚少。

    编辑:是的,阅读了额外的信息后,我认为这没关系。您可能还应该有一个关闭线程池的方法。

    【讨论】:

    • 谢谢,这有帮助。我用这段代码的具体功能更新了 OP。我认为让构造函数创建线程池是有意义的,但我对此很陌生,欢迎任何我能得到的建议:)
    • @kyena:好的,已编辑答案。基本上它看起来可能没问题。您的课程实际上是成为一个线程池(通过组合)。
    • 谢谢!感谢您的帮助:)
    【解决方案2】:

    我同意乔恩的观点。

    此外,让我指出您实际上并没有在构造函数中对线程池启动任何操作。您正在实例化线程池,但此时它没有要运行的任务。因此,正如所写的,在该实例完成构建之前,您不会有任何东西开始对其进行操作。

    【讨论】:

      【解决方案3】:

      听起来线程池将由对象拥有和使用;线程不会从对象中传递出去。如果是这样,那应该不是问题。

      构造函数创建一个对象并初始化它的状态。我无法想象需要长时间运行的进程才能这样做的用例。

      我可以看到对象可能与线程池交互以完成任务的位置,但我不认为该对象需要拥有线程池。

      更多细节可能会有所帮助。

      【讨论】:

      • 谢谢,这有帮助。我用这段代码专门针对线程池所做的更新了 OP。我认为让这个对象拥有线程池是有道理的,但我对此很陌生,欢迎任何我能得到的建议:)
      【解决方案4】:

      我认为只要该对象完全管理该线程池的生命周期,就可以在对象的构造函数中启动一个线程池。

      如果你走这条路,你将不得不加倍努力以提供以下保证:

      1. 如果您的构造函数抛出任何异常(运行时和已检查),您必须在构造函数中包含关闭线程池的清理代码。如果您不这样做并使用非守护线程创建一个线程池,那么,例如,使用您的对象的一个​​小控制台程序可能会永远保持运行,从而泄漏宝贵的系统资源。
      2. 您需要提供我称之为destructor 方法的东西,类似于Java I/O 中的close。我通常称之为releaseResources。请注意,finalize 不能替代此方法,因为它由 GC 调用,并且对于内存占用相当小的对象,它可能永远不会被调用。
      3. 使用此对象时请遵循此模式

      ->

      MyThreadPoolContainer container =
          new MyThreadPoolContainer( ... args to initialize the object... );
      
      try
      {
        methodThatUsesContainer( container );
      }
      finally
      {
        container.releaseResources( );
      }
      
      1. 记录对象构造函数分配有限资源并且必须显式调用destructor 方法以防止其泄漏。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2019-04-19
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2013-09-11
        • 2015-07-24
        相关资源
        最近更新 更多