bru*_*nde 182
我认为文档很好地解释了这两个函数的区别和用法:
创建一个线程池,该线程池重用在共享的无界队列中运行的固定数量的线程.在任何时候,最多nThreads线程将是活动的处理任务.如果在所有线程都处于活动状态时提交了其他任务,则它们将在队列中等待,直到线程可用.如果任何线程由于在关闭之前执行期间的故障而终止,则在需要执行后续任务时将使用新的线程.池中的线程将一直存在,直到它被明确关闭.
创建一个根据需要创建新线程的线程池,但在它们可用时将重用先前构造的线程.这些池通常会提高执行许多短期异步任务的程序的性能.如果可用,执行调用将重用先前构造的线程.如果没有可用的现有线程,则将创建一个新线程并将其添加到池中.未使用60秒的线程将终止并从缓存中删除.因此,长时间闲置的池不会消耗任何资源.请注意,可以使用ThreadPoolExecutor构造函数创建具有相似属性但不同详细信息的池(例如,超时参数).
在资源方面,newFixedThreadPool将保持所有线程运行,直到它们被明确终止.在newCachedThreadPool未使用60秒的线程中终止并从缓存中删除.
鉴于此,资源消耗将在很大程度上取决于具体情况.例如,如果你有大量长时间运行的任务,我建议你FixedThreadPool.至于CachedThreadPool,文档说"这些池通常会提高执行许多短期异步任务的程序的性能".
Lou*_* F. 66
为了完成其他答案,我想引用Joshua Bloch撰写的Effective Java,第2版,第10章,第68项:
"为特定应用程序选择执行程序服务可能很棘手.如果你正在编写一个小程序或一个负载很轻的服务器,使用Executors.new- CachedThreadPool通常是一个不错的选择,因为它不需要配置,通常"正确的事情."但是对于负载很重的生产服务器来说,缓存的线程池不是一个好的选择!
在缓存的线程池中,提交的任务不会排队,而是立即传递给线程执行.如果没有可用的线程,则创建一个新线程.如果服务器负载过重以至于所有CPU都被充分利用,并且更多任务到达,则会创建更多线程,这只会使事情变得更糟.
因此,在负载很重的生产服务器中,最好使用Executors.newFixedThreadPool,它为您提供具有固定线程数的池,或直接使用ThreadPoolExecutor类,以实现最大程度的控制."
Ali*_*ani 13
本ThreadPoolExecutor类是由许多的返回执行人基实现Executors工厂方法。因此,让我们从的角度来处理Fixed和Cached线程池ThreadPoolExecutor。
此类的主要构造函数如下所示:
public ThreadPoolExecutor(
int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler
)
Run Code Online (Sandbox Code Playgroud)
在corePoolSize确定目标线程池的最小大小。即使没有要执行的任务,该实现也会维护一个该大小的池。
的maximumPoolSize是,可以一次活动线程的最大数目。
当线程池增长并大于corePoolSize阈值后,执行器可以终止空闲线程并corePoolSize再次到达。如果allowCoreThreadTimeOut为真,那么如果核心池线程空闲超过keepAliveTime阈值,则执行器甚至可以终止它们。
所以底线是如果线程保持空闲超过keepAliveTime阈值,它们可能会被终止,因为对它们没有需求。
当一个新任务进来并且所有核心线程都被占用时会发生什么?新任务将在该BlockingQueue<Runnable>实例内排队。当一个线程空闲时,可以处理这些排队任务之一。
BlockingQueueJava 中有不同的接口实现,因此我们可以实现不同的排队方法,例如:
有界队列:新任务将在有界任务队列中排队。
无界队列:新任务将在无界任务队列中排队。所以这个队列可以在堆大小允许的范围内增长。
同步切换:我们也可以使用SynchronousQueue来排队新任务。在这种情况下,当排队一个新任务时,另一个线程必须已经在等待该任务。
以下是ThreadPoolExecutor执行新任务的方式:
corePoolSize线程正在运行,则尝试以给定任务作为其第一个作业启动一个新线程。BlockingQueue#offer方法将新任务加入队列
。offer如果队列已满,该方法不会阻塞并立即返回false。offer返回false),那么它会尝试将此任务作为其第一个作业添加到线程池中的新线程。RejectedExecutionHandler.固定线程池和缓存线程池之间的主要区别归结为以下三个因素:
+-----------+-----------+-----------+----- -----------------------------+ | 泳池类型 | 磁芯尺寸 | 最大尺寸 | 排队策略 | +-----------+-----------+-----------+----- -----------------------------+ | 固定 | n(固定)| n(固定)| 无界`LinkedBlockingQueue` | +-----------+-----------+-----------+----- -----------------------------+ | 缓存 | 0 | 整数.MAX_VALUE | `同步队列` | +-----------+-----------+-----------+----- -----------------------------+
Excutors.newFixedThreadPool(n)工作原理:
+-----------+-----------+-------------------+---------------------------------+ | Pool Type | Core Size | Maximum Size | Queuing Strategy | +-----------+-----------+-------------------+---------------------------------+ | Fixed | n (fixed) | n (fixed) | Unbounded `LinkedBlockingQueue` | +-----------+-----------+-------------------+---------------------------------+ | Cached | 0 | Integer.MAX_VALUE | `SynchronousQueue` | +-----------+-----------+-------------------+---------------------------------+
如你看到的:
OutOfMemoryError.我什么时候应该使用其中一种?哪种策略在资源利用方面更好?
当我们出于资源管理目的而限制并发任务的数量时,固定大小的线程池似乎是一个不错的选择。
例如,如果我们要使用 executor 来处理 Web 服务器请求,则固定的 executor 可以更合理地处理请求突发。
为了更好的资源管理,强烈建议创建一个ThreadPoolExecutor有界BlockingQueue<T>实现和合理的RejectedExecutionHandler.
这是Executors.newCachedThreadPool()工作原理:
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
Run Code Online (Sandbox Code Playgroud)
如你看到的:
Integer.MAX_VALUE. 实际上,线程池是无界的。SynchronousQueue当另一端没有人接受时,向 a 提供新任务总是失败!我什么时候应该使用其中一种?哪种策略在资源利用方面更好?
当您有很多可预测的短期运行任务时使用它。
krm*_*007 12
如果你看到grepcode中的代码,你会看到,他们正在调用ThreadPoolExecutor.在内部并设置其属性.您可以创建一个以更好地控制您的需求.
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
Run Code Online (Sandbox Code Playgroud)
如果您不担心Callable/Runnable任务的无限队列,您可以使用其中一个.正如布鲁诺建议,我也喜欢newFixedThreadPool到newCachedThreadPool这两者之间.
但ThreadPoolExecutor的 规定相比,它更灵活的功能newFixedThreadPool或newCachedThreadPool
ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime,
TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory,
RejectedExecutionHandler handler)
Run Code Online (Sandbox Code Playgroud)
好处:
您可以完全控制BlockingQueue的大小.与之前的两个选项不同,它并不是无限制的.由于系统中出现意外动荡的大量待处理Callable/Runnable任务,我不会出现内存错误.
您可以实施自定义拒绝处理策略或使用以下策略之一:
在默认情况下ThreadPoolExecutor.AbortPolicy,处理程序在拒绝时抛出运行时RejectedExecutionException.
在ThreadPoolExecutor.CallerRunsPolicy,调用execute本身的线程运行任务.这提供了一种简单的反馈控制机制,可以降低新任务的提交速度.
在ThreadPoolExecutor.DiscardPolicy,简单地删除无法执行的任务.
在ThreadPoolExecutor.DiscardOldestPolicy,如果执行程序未关闭,则删除工作队列头部的任务,然后重试执行(可能再次失败,导致重复执行).
您可以为以下用例实现自定义线程工厂:
小智 8
这是正确的,Executors.newCachedThreadPool()对于为多个客户端和并发请求提供服务的服务器代码来说,它不是一个很好的选择.
为什么?它基本上有两个(相关的)问题:
它是无界限的,这意味着你只需要在服务中注入更多的工作(DoS攻击),就可以让任何人瘫痪你的JVM.线程消耗不可忽略的内存量,并且还会根据正在进行的工作增加内存消耗,因此以这种方式推倒服务器非常容易(除非您有其他断路器).
Executor面向一个事实会加剧无限问题,SynchronousQueue这意味着任务提供者和线程池之间存在直接切换.如果所有现有线程都忙,则每个新任务都将创建一个新线程.这通常是服务器代码的糟糕策略.当CPU饱和时,现有任务需要更长时间才能完成.然而,正在提交更多任务并创建更多线程,因此任务需要更长时间才能完成.当CPU饱和时,更多线程绝对不是服务器所需要的.
以下是我的建议:
使用固定大小的线程池Executors.newFixedThreadPool或 ThreadPoolExecutor.设置最大线程数;
仅当您有 Javadoc 中所述的短期异步任务时,才必须使用 newCachedThreadPool,如果您提交需要较长时间处理的任务,您最终将创建太多线程。如果您以更快的速度向 newCachedThreadPool ( http://rashcoder.com/be-careful-while-using-executors-newcachedthreadpool/ )提交长时间运行的任务,您可能会达到 100% CPU 。