【问题标题】:SingleThreadExecutor VS plain threadSingleThreadExecutor VS 普通线程
【发布时间】:2018-10-09 03:36:04
【问题描述】:

除了Executor 接口比普通线程有一些优势(例如管理)之外,这样做之间是否存在任何真正的内部差异(大的性能差异、资源消耗......):

ExecutorService executor = Executors.newSingleThreadExecutor();
executor.submit(runnable);

还有:

Thread thread = new Thread(runnable);
thread.start();

我在这里只问一个线程。

【问题讨论】:

  • 在我的问题中,我只询问了一个单线程执行器。我明白使用多线程的好处
  • 将编辑问题以使其清楚
  • 在第二个答案中:“即使对于单个线程,我更喜欢使用 Executors.newFixedThreadPool(1);”。这是为什么?只是因为您使用的是 ExecutorService 而方便吗?还是有其他原因?
  • 你正在执行多少个Runnable

标签: java multithreading threadpoolexecutor


【解决方案1】:

主要区别在于任务执行策略。

通过创建Thread 实例或子类化Thread,您基本上是在执行单个任务。

另一方面,使用Executors.newSingleThreadExecutor() 允许您提交多个任务。由于保证这些任务不会同时执行,因此您可以利用以下线程限制优势:

  • 访问非线程安全的对象时不需要同步
  • 一个任务的记忆效果保证对下一个任务可见

【讨论】:

    【解决方案2】:

    如果只执行一个Runnable,那么它们之间没有太大区别

    使用普通线程可能会更高效一些,因为创建ExecutorService (例如ThreadPoolExecutor)除了创建新线程之外还有其他事情要做。例如,创建阻塞队列、创建策略,虽然这些事情都是隐式完成的。

    并且你必须在这个runnable被执行之后shutdown执行者。否则此池中的单个线程将永远不会退出。

    【讨论】:

      【解决方案3】:

      Executors#newSingleThreadExecutor() 在后台创建 ThreadPoolExecutor 对象,
      在此处查看代码:http://www.docjar.com/html/api/java/util/concurrent/Executors.java.html

        133       public static ExecutorService newSingleThreadExecutor() {
        134           return new FinalizableDelegatedExecutorService
        135               (new ThreadPoolExecutor(1, 1,
        136                                       0L, TimeUnit.MILLISECONDS,
        137                                       new LinkedBlockingQueue<Runnable>()));
        138       }
      

      documentation of ThreadPoolExecutor 解释了它在哪些情况下具有优势:

      线程池解决了两个不同的问题:它们通常提供 执行大量异步时提高性能 任务,由于减少了每个任务的调用开销,并且它们提供了一个 限制和管理资源的方法,包括线程, 执行任务集合时消耗。每个 ThreadPoolExecutor 还维护了一些基本的统计信息,比如完成的数量 任务。

      如果您只需要偶尔运行一次单线程(比如每小时一次),那么在性能方面,使用ThreadPoolExecutor 可能会更慢,因为您需要实例化整个机器(池 +线程),然后将其从内存中丢弃。

      但是如果你想经常使用这个单线程(比如每 15 秒),那么优点是你只创建一次池和线程,将它保存在内存中,然后全部使用节省不时创建一个新线程的时间(这可能会非常昂贵,如果您想每 15 秒左右使用一次)。

      【讨论】:

      • 当我询问性能差异以及我是否遗漏了什么时,我认为这是正确的答案。不过一般来说很好的答案。谢谢
      • 拥有ExecutorService/ThreadPoolExecutor 的另一个优点是它允许您提交任务并获得Future。如果您加入Future,您可以确保没有未捕获的异常。使用普通的Thread,您需要注意未捕获的异常,并且可能需要使用Thread.setUncaughtExceptionHandler
      【解决方案4】:

      这是一个抽象,而那些总是以“成本”为代价的:

      • 一些(潜在的)“性能损失”
      • 减少“控制”的数量(这就是重点 - 您不需要处理低级别的细节,所以,如果必须,...)

      主要区别在于该服务允许您提交多个任务,而线程只能运行一个 Runnable。另一方面,您必须担心诸如“关闭”服务之类的事情。

      经验法则:性能方面在这里应该接近“可忽略”。因此,您更喜欢“更抽象”的执行器服务解决方案。因为这使您可以将关注点与实际线程分开。更重要的是:如果您曾经选择对该服务使用不同类型的实现......您的其余代码不需要关心这一点。

      长话短说:抽象成本,但在这种情况下,您通常更喜欢“更抽象”的解决方案。因为最终,这会降低您的解决方案的复杂性。

      【讨论】:

        猜你喜欢
        • 2016-05-13
        • 1970-01-01
        • 1970-01-01
        • 2015-10-05
        • 2021-05-26
        • 2015-04-22
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多