使用ExecutorService
而不是运行将Runnable
传递给Thread
构造函数的线程有什么好处?
答案 0 :(得分:40)
ExecutorService
抽象出与低级抽象相关的许多复杂性,如原始Thread
。它提供了安全启动,关闭,提交,执行和阻止成功或突然终止任务的机制(表示为Runnable
或Callable
)。
来自JCiP,第6.2节,直接来自马的嘴:
Executor
可能是一个简单的接口,但它构成了一个灵活而强大的异步任务执行框架的基础,支持各种任务执行策略。它提供了从任务执行中解除任务提交的标准方法,将任务描述为Runnable
。Executor
实现还提供生命周期支持和挂钩,用于添加统计信息收集,应用程序管理和监视。 ... 使用Executor
通常是在您的应用程序中实现生产者 - 消费者设计的最简单途径。
j.u.concurrent
框架可以让您专注于构建任务,依赖关系,潜在的并行性,而不是花时间(经常错误地,并且非常努力地)实现并行的底层基础架构。对于大量并发应用程序,可以直接识别和利用任务边界并使用j.u.c
,从而使您可以专注于真正并发性挑战的更小的子集,这可能需要更专业的解决方案。
此外,尽管样板外观和感觉,Oracle API page summarizing the concurrency utilities包含一些非常可靠的使用它们的论据,尤其是:
开发人员可能已经 了解标准库 上课,所以没有必要学习 ad-hoc的API和行为 并发组件。另外, 并发应用程序远 它们构建时更容易调试 在可靠,经过良好测试的组件上。
这question on SO询问一本好书,答案是JCiP。如果你还没有,请给自己一份。这种全面的并发方法远远超出了这个问题,从长远来看,这将为您节省很多心痛。
答案 1 :(得分:18)
我看到的一个优点是管理/调度多个线程。使用ExecutorService,您不必编写自己的线程管理器,这可能会受到bug的困扰。如果您的程序需要一次运行多个线程,这将特别有用。例如,您希望一次执行两个线程,您可以轻松地执行此操作:
ExecutorService exec = Executors.newFixedThreadPool(2);
exec.execute(new Runnable() {
public void run() {
System.out.println("Hello world");
}
});
exec.shutdown();
这个例子可能很简单,但是试着认为“hello world”行包含繁重的操作,并且你希望一次在多个线程中运行该操作以提高程序的性能。这只是一个例子,仍有许多情况需要安排或运行多个线程并使用ExecutorService作为线程管理器。
对于运行单个线程,我没有看到使用ExecutorService的任何明显优势。
答案 2 :(得分:10)
传统线程的以下限制克服了Executor框架(内置线程池框架)。
StackOverflowException
异常,因此我们的JVM将崩溃。线程池的好处
使用线程池可以避免在请求或任务处理期间创建线程,从而缩短响应时间。
使用线程池可以根据需要更改执行策略。只需替换ExecutorService实现,就可以从单线程转到多线程。
Java应用程序中的线程池通过创建基于系统负载和可用资源决定的配置数量的线程来提高系统的稳定性。
线程池将应用程序开发人员从线程管理中解放出来,并且可以专注于业务逻辑。
答案 3 :(得分:7)
以下是一些好处:
答案 4 :(得分:2)
创建新线程真的那么贵吗?
作为基准测试,我刚刚用Runnable
s用空run()
方法创建了60,000个线程。创建每个线程后,我立即调用了它的start(..)
方法。这需要大约30秒的强烈CPU活动。已经针对this question进行了类似的实验。这些的总结是,如果线程没有立即完成,并且大量的活动线程累积(几千),那么就会出现问题:(1)每个线程都有一个堆栈,所以你的内存耗尽,(2)操作系统强加的每个进程的线程数可能有限制,但not necessarily, it seems。
所以,据我所知,如果我们谈论每秒发送10个线程,并且它们都比新的线程更快完成,我们可以保证不会超过这个速率,那么ExecutorService在可见性能或稳定性方面没有任何具体优势。 (虽然在代码中表达某些并发思想可能仍然会使它更方便或可读。)另一方面,如果你可能每秒安排数百或数千个任务,这需要时间来运行,你可能会遇到大问题马上。这可能意外地发生,例如,如果您创建线程以响应对服务器的请求,并且服务器接收的请求强度会出现峰值。但是例如一个线程响应每个用户输入事件(按键,鼠标移动)似乎完全没问题,只要任务很简单。
答案 5 :(得分:1)
ExecutorService还允许访问FutureTask,后者将在完成后台任务的结果后返回给调用类。在实现Callable
的情况下public class TaskOne implements Callable<String> {
@Override
public String call() throws Exception {
String message = "Task One here. . .";
return message;
}
}
public class TaskTwo implements Callable<String> {
@Override
public String call() throws Exception {
String message = "Task Two here . . . ";
return message;
}
}
// from the calling class
ExecutorService service = Executors.newFixedThreadPool(2);
// set of Callable types
Set<Callable<String>>callables = new HashSet<Callable<String>>();
// add tasks to Set
callables.add(new TaskOne());
callables.add(new TaskTwo());
// list of Future<String> types stores the result of invokeAll()
List<Future<String>>futures = service.invokeAll(callables);
// iterate through the list and print results from get();
for(Future<String>future : futures) {
System.out.println(future.get());
}
答案 6 :(得分:0)
在java 1.5版本之前,Thread / Runnable是为两个单独的服务设计的
ExecutorService通过将Runnable / Callable指定为工作单元并将Executor指定为执行工作单元(具有生命周期)来解耦这两个服务
答案 7 :(得分:0)
创建大量线程而不限制最大阈值可能导致应用程序耗尽堆内存。因为创建一个ThreadPool是更好的解决方案。使用ThreadPool,我们可以限制可以合并和重用的线程数。
Executors框架有助于在java中创建线程池的过程。 Executors类使用ThreadPoolExecutor提供ExecutorService的简单实现。
<强>来源:强>
答案 8 :(得分:0)
执行器框架
//Task
Runnable someTask = new Runnable() {
@Override
public void run() {
System.out.println("Hello World!");
}
};
//Thread
Thread thread = new Thread(someTask);
thread.start();
//Executor
Executor executor = new Executor() {
@Override
public void execute(Runnable command) {
Thread thread = new Thread(someTask);
thread.start();
}
};
Executor
只是一个接受 Runnable
的接口。 execute()
方法可以只调用 command.run()
或与其他使用 Runnable
的类一起工作(例如线程)
interface Executor
execute(Runnable command)
ExecutorService
接口扩展 Executor
并添加管理方法 - shutdown()
和 submit()
返回 Future
[About] - {{1} }, get()
cancel()
interface ExecutorService extends Executor
Future<?> submit(Runnable task)
shutdown()
...
扩展 ScheduledExecutorService
用于计划执行任务
ExecutorService
interface ScheduledExecutorService extends ExecutorService
schedule()
类是一个工厂,用于为运行 Executors
个任务[About]
ExecutorService
实现
async
结论
使用 class Executors
newFixedThreadPool() returns ThreadPoolExecutor
newCachedThreadPool() returns ThreadPoolExecutor
newSingleThreadExecutor() returns FinalizableDelegatedExecutorService
newWorkStealingPool() returns ForkJoinPool
newSingleThreadScheduledExecutor() returns DelegatedScheduledExecutorService
newScheduledThreadPool() returns ScheduledThreadPoolExecutor
...
对 CPU 和内存来说是一项代价高昂的操作。
Thread
由 Task Queue(ThreadPoolExecutor
) 和 Thread Pool(Set of BlockingQueue
) 组成,它们具有更好的性能和 API 来处理异步任务