Executor
接口具有一些优点(例如管理)之外,执行以下哪种方式是否存在真正的内部差异(大的性能差异,资源消耗等...):ExecutorService executor = Executors.newSingleThreadExecutor();
executor.submit(runnable);
并且:
Thread thread = new Thread(runnable);
thread.start();
我这里只询问一个线程。
Executor
接口具有一些优点(例如管理)之外,执行以下哪种方式是否存在真正的内部差异(大的性能差异,资源消耗等...):ExecutorService executor = Executors.newSingleThreadExecutor();
executor.submit(runnable);
并且:
Thread thread = new Thread(runnable);
thread.start();
我这里只询问一个线程。
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 }
ThreadPoolExecutor 的文档 解释了在什么情况下它会带来好处:
线程池解决了两个不同的问题:当执行大量异步任务时,由于减少了每个任务调用的开销,它们通常提供更好的性能,并且它们提供了一种绑定和管理资源(包括线程)的方法,在执行任务集合时消耗这些资源。每个 ThreadPoolExecutor 还会维护一些基本统计信息,例如完成的任务数。
如果你只需要偶尔运行单个线程(例如每小时一次),那么就性能而言,使用 ThreadPoolExecutor
可能会更慢,因为你需要实例化整个机制(池+线程),然后将其从内存中丢弃。
但是,如果您经常使用此单个线程(例如每 15 秒钟一次),则优点在于您只需创建一次池和线程,将其保存在内存中,并在所有时间内使用它,节省了每隔一段时间创建新线程的时间(如果您想要每 15 秒钟使用它,则可能相当昂贵)。
ExecutorService
/ThreadPoolExecutor
的另一个优点是它允许您提交任务并获取Future
。如果您加入了Future
,则可以确保没有未捕获的异常。对于普通的Thread
,您需要注意未捕获的异常,并可能希望使用Thread.setUncaughtExceptionHandler
。 - cambunctious主要的区别在于任务执行策略。
创建一个Thread
实例或者继承Thread
类,基本上是执行单个任务。
而另一方面使用Executors.newSingleThreadExecutor()
允许您提交多个任务。由于这些任务保证不会并发执行,因此可以利用以下线程封闭(thread confinement)的优势:
这是一个抽象概念,但这种抽象常常伴随着一定的“代价”:
主要区别在于该服务使您能够提交多个任务,而线程只能运行一个Runnable。另一方面,您需要担心的事情就是“关闭”服务之类的问题。
一个经验法则:在此处应该将性能方面的影响视为“可以忽略不计”。因此,您更喜欢“更抽象”的执行器服务解决方案。因为这样可以将您的关注点与实际线程分离开来。更重要的是:如果您选择使用不同的服务实现方式... 您的其余代码不需要关心这个问题。
长话短说:抽象概念固然有代价,但在这种情况下,您通常更喜欢“更抽象”的解决方案。因为最终这可以降低您的解决方案的复杂性。
Runnable
,那么它们之间没有太大的区别。ExecutorService
(如ThreadPoolExecutor
)除了创建新线程外还要做一些其他事情,例如创建阻塞队列、创建策略,尽管这些都是隐式完成的。Runnable
执行完毕后,必须关闭
执行器。否则,池中的单个线程将永远无法退出。