常见的4种线程池

本文深入探讨了Android中线程池的实现原理,重点介绍了四种常见线程池:FixThreadPool、CachedThreadPool、ScheduledThreadPool和SingleThreadPool的特点及应用场景,通过生动的比喻帮助读者理解其工作流程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

原文:http://blog.youkuaiyun.com/seu_calvin/article/details/52415337
使用线程池可以给我们带来很多好处,首先通过线程池中线程的重用,减少创建和销毁线程的性能开销。其次,能控制线程池中的并发数,否则会因为大量的线程争夺CPU资源造成阻塞。最后,线程池能够对线程进行管理,比如使用ScheduledThreadPool来设置延迟N秒后执行任务,并且每隔M秒循环执行一次。

下面会通过介绍线程池中的真正实现者——ThreadPoolExecutor来引出Android中的4类线程池的使用以及特性分析,会加上笔者自己的理解,和自认为比较恰当的比喻,帮助理解。

1.凡事得靠ThreadPoolExecutor(铺垫环节,懂的直接跳过)

Executor作为一个接口,它的具体实现就是ThreadPoolExecutor。

Android中的线程池都是直接或间接通过配置ThreadPoolExecutor来实现不同特性的线程池。

先介绍ThreadPoolExecutor的一个常用的构造方法。

/*
*@ ThreadPoolExecutor构造参数介绍
*@author SEU_Calvin
* @date 2016/09/03
*/
public ThreadPoolExecutor(
//核心线程数,除非allowCoreThreadTimeOut被设置为true,否则它闲着也不会死
int corePoolSize, 
//最大线程数,活动线程数量超过它,后续任务就会排队                   
int maximumPoolSize, 
//超时时长,作用于非核心线程(allowCoreThreadTimeOut被设置为true时也会同时作用于核心线程),闲置超时便被回收           
long keepAliveTime,                          
//枚举类型,设置keepAliveTime的单位,有TimeUnit.MILLISECONDS(ms)、TimeUnit. SECONDS(s)等
TimeUnit unit,
//缓冲任务队列,线程池的execute方法会将Runnable对象存储起来
BlockingQueue<Runnable> workQueue,
//线程工厂接口,只有一个new Thread(Runnable r)方法,可为线程池创建新线程
ThreadFactory threadFactory)

ThreadPoolExecutor的各个参数所代表的特性注释中已经写的很清楚了,那么ThreadPoolExecutor执行任务时的心路历程是什么样的呢?(以下用currentSize表示线程池中当前线程数量)

(1)当currentSize<corePoolSize时,没什么好说的,直接启动一个核心线程并执行任务。

(2)当currentSize>=corePoolSize、并且workQueue未满时,添加进来的任务会被安排到workQueue中等待执行。

(3)当workQueue已满,但是currentSize<maximumPoolSize时,会立即开启一个非核心线程来执行任务。

(4)当currentSize>=corePoolSize、workQueue已满、并且currentSize>maximumPoolSize时,调用handler默认抛出RejectExecutionExpection异常。

2. Android中的四类线程池

Android中最常见的四类具有不同特性的线程池分别为FixThreadPool、CachedThreadPool、ScheduleThreadPool以及SingleThreadExecutor。

2.1 FixThreadPool(一堆人排队上公厕)

/*
*@FixThreadPool介绍
*@author SEU_Calvin
* @date 2016/09/03
*/
public static ExecutorService newFixThreadPool(int nThreads){
    return new ThreadPoolExecutor(nThreads, nThreads, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>());
}
//使用
Executors.newFixThreadPool(5).execute(r);

(1)从配置参数来看,FixThreadPool只有核心线程,并且数量固定的,也不会被回收,所有线程都活动时,因为队列没有限制大小,新任务会等待执行。

(2)【前方高能,笔者脑洞】FixThreadPool其实就像一堆人排队上公厕一样,可以无数多人排队,但是厕所位置就那么多,而且没人上时,厕所也不会被拆迁,哈哈o(∩_∩)o ,很形象吧。

(3)由于线程不会回收,FixThreadPool会更快地响应外界请求,这也很容易理解,就好像有人突然想上厕所,公厕不是现用现建的。

2.2 SingleThreadPool(公厕里只有一个坑位)

/*
*@SingleThreadPool介绍
*@author SEU_Calvin
* @date 2016/09/03
*/
public static ExecutorService newSingleThreadPool (int nThreads){
    return new FinalizableDelegatedExecutorService ( new ThreadPoolExecutor (1, 1, 0, TimeUnit. MILLISECONDS, new LinkedBlockingQueue<Runnable>()) );
}
//使用
Executors.newSingleThreadPool ().execute(r);

(1)从配置参数可以看出,SingleThreadPool只有一个核心线程,确保所有任务都在同一线程中按顺序完成。因此不需要处理线程同步的问题。

(2)【前方高能,笔者脑洞】可以把SingleThreadPool简单的理解为FixThreadPool的参数被手动设置为1的情况,即Executors.newFixThreadPool(1).execute®。所以SingleThreadPool可以理解为公厕里只有一个坑位,先来先上。为什么只有一个坑位呢,因为这个公厕是收费的,收费的大爷上年纪了,只能管理一个坑位,多了就管不过来了(线程同步问题)。

2.3 CachedThreadPool(一堆人去一家很大的咖啡馆喝咖啡)

/*
*@CachedThreadPool介绍
*@author SEU_Calvin
* @date 2016/09/03
*/
public static ExecutorService newCachedThreadPool(int nThreads){
    return new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60L, TimeUnit. SECONDS, new SynchronousQueue<Runnable>());
}
//使用
Executors.newCachedThreadPool().execute(r);

(1)CachedThreadPool只有非核心线程,最大线程数非常大,所有线程都活动时,会为新任务创建新线程,否则利用空闲线程(60s空闲时间,过了就会被回收,所以线程池中有0个线程的可能)处理任务。

(2)任务队列SynchronousQueue相当于一个空集合,导致任何任务都会被立即执行。

(3)【前方高能,笔者脑洞】CachedThreadPool就像是一堆人去一个很大的咖啡馆喝咖啡,里面服务员也很多,随时去,随时都可以喝到咖啡。但是为了响应国家的“光盘行动”,一个人喝剩下的咖啡会被保留60秒,供新来的客人使用,哈哈哈哈哈,好恶心啊。如果你运气好,没有剩下的咖啡,你会得到一杯新咖啡。但是以前客人剩下的咖啡超过60秒,就变质了,会被服务员回收掉。

(4)比较适合执行大量的耗时较少的任务。喝咖啡人挺多的,喝的时间也不长。

2.4 ScheduledThreadPool(4个里面唯一一个有延迟执行和周期重复执行的线程池)

/*
*@ScheduledThreadPool介绍
*@author SEU_Calvin
* @date 2016/09/03
*/
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize){
return new ScheduledThreadPoolExecutor(corePoolSize);
}
public ScheduledThreadPoolExecutor(int corePoolSize){
super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS, new DelayedQueue ());
}
//使用,延迟1秒执行,每隔2秒执行一次Runnable r
Executors. newScheduledThreadPool (5).scheduleAtFixedRate(r, 1000, 2000, TimeUnit.MILLISECONDS);

(1)核心线程数固定,非核心线程(闲着没活干会被立即回收)数没有限制。

(2)从上面代码也可以看出,ScheduledThreadPool主要用于执行定时任务以及有固定周期的重复任务。

至此Android中最常见的四类不同特性的线程池内容总结完毕。

<think>好的,我现在需要回答用户关于Java中常见线程池类型及其用途的问题。首先,用户提到了线程池的核心概念和优势,引用了四个参考资料。我需要结合这些引用内容,整理出Java中常见线程池类型,并说明它们的特点和应用场景。 首先,根据引用[1],线程池通过ExecutorService实现,可以降低线程创建和销毁的开销。引用[3]详细解释了corePoolSize和maximumPoolSize的作用,以及线程池如何根据任务数量动态调整线程数量。引用[4]提到了不同线程池的适用场景,比如IO密集型、计算密集型和混合型任务,还提供了万能公式来计算corePoolSize。 接下来,我需要回忆Java中通过Executors类提供的几种常见线程池: 1. **FixedThreadPool**:固定线程数的线程池。适用于负载较重的服务器,需要限制线程数量以避免资源耗尽。引用[3]提到corePoolSize和maximumPoolSize相等,所以这里应该是固定大小的。 2. **CachedThreadPool**:可缓存线程池,线程数可扩展。适合执行很多短期异步任务,或者负载较轻的服务器。当任务增加时,会创建新线程,空闲线程会被回收。这里需要确认是否maximumPoolSize是Integer.MAX_VALUE,队列是SynchronousQueue,如引用[4]提到的同步队列快速扩容。 3. **SingleThreadExecutor**:单线程的线程池,保证任务顺序执行。适用于需要顺序执行任务的场景,如日志记录。 4. **ScheduledThreadPool**:支持定时或周期性任务执行的线程池。适用于需要调度任务的场景,比如定时任务。 另外,用户还提到了ThreadPoolExecutor,这是线程池的基础实现类,其他线程池都是通过配置它的参数来实现的。需要说明参数如corePoolSize、maximumPoolSize、keepAliveTime、workQueue等的影响。 应用场景部分需要结合引用[4]中的分类,比如IO密集型使用CachedThreadPool,计算密集型使用FixedThreadPool,混合型使用自定义参数。还要提到线程池的配置建议,比如万能公式。 在回答结构上,需要分点列出每种线程池,说明其实现方式、参数配置、特点和应用场景。最后总结如何根据任务类型选择合适的线程池,并提到线程池的配置建议。 需要确保使用正确的LaTeX格式,行内公式用$...$,独立公式用$$...$$,但根据用户提供的引用,这里可能不需要太多数学公式,更多是参数说明。所以可能只需在引用公式时使用,比如引用[4]中的公式。 最后生成相关问题,可能需要包括线程池参数配置、线程池的工作流程、如何避免资源耗尽、以及不同任务类型线程池选择等问题。 检查是否有遗漏的信息,比如引用[2]提到线程池流程,可能在回答中简要提及线程池的工作机制,但用户的问题集中在类型和用途,所以可能不需要深入流程细节,但可以提到核心参数如队列类型的影响。 现在组织语言,确保每个线程池类型都清晰说明,并正确引用相关文献,如引用[3]解释corePoolSize和maximumPoolSize的关系,引用[4]说明参数配置公式等。</think>### Java常见线程池类型及其应用场景 Java通过`java.util.concurrent.Executors`提供了多种线程池实现,每种类型针对不同任务场景设计。以下为四种典型线程池及其用途: --- #### 1. **固定大小线程池(FixedThreadPool)** - **实现方式**: ```java Executors.newFixedThreadPool(int nThreads) ``` 核心线程数(`corePoolSize`)与最大线程数(`maximumPoolSize`)相等,任务队列使用无界的`LinkedBlockingQueue`[^3]。 - **特点**: - 线程数量固定,不会因任务激增而扩容。 - 任务队列无界,可能引发内存溢出(OOM)。 - **应用场景**: 适用于**计算密集型任务**(如复杂运算),或需严格控制并发数的场景(如高负载服务器)[^4]。 --- #### 2. **缓存线程池(CachedThreadPool)** - **实现方式**: ```java Executors.newCachedThreadPool() ``` 核心线程数为0,最大线程数为`Integer.MAX_VALUE`,任务队列为`SynchronousQueue`(直接传递任务,不存储)。 - **特点**: - 线程数量根据任务动态伸缩,空闲线程60秒后回收。 - 高并发时可能创建大量线程,导致资源耗尽。 - **应用场景**: 适用于**短时异步任务**或**IO密集型任务**(如HTTP请求处理),任务执行时间短且频率波动大[^4]。 --- #### 3. **单线程池(SingleThreadExecutor)** - **实现方式**: ```java Executors.newSingleThreadExecutor() ``` 仅1个核心线程,任务队列为无界`LinkedBlockingQueue`。 - **特点**: - 保证任务按提交顺序执行。 - 无界队列同样存在OOM风险。 - **应用场景**: 适用于需**顺序执行任务**的场景(如日志顺序写入、单线程GUI更新)[^1]。 --- #### 4. **定时任务线程池(ScheduledThreadPool)** - **实现方式**: ```java Executors.newScheduledThreadPool(int corePoolSize) ``` 使用`DelayedWorkQueue`支持延迟或周期性任务。 - **特点**: - 可调度任务在固定延迟或周期执行。 - 核心线程数固定,但空闲线程不会被回收。 - **应用场景**: 适用于**定时任务**(如数据定时同步、心跳检测)。 --- ### **ThreadPoolExecutor 参数配置** 自定义线程池需配置以下参数: - **`corePoolSize`**:核心线程数(长期保留的线程)。 - **`maximumPoolSize`**:最大线程数(突发任务时临时扩容)。 - **`workQueue`**:任务队列类型(影响任务排队策略)。 - **`keepAliveTime`**:非核心线程空闲回收时间。 - **`threadFactory`**:线程创建工厂(可定制线程属性)。 - **`handler`**:拒绝策略(任务队列满时的处理方式)。 --- ### **线程池选择建议** - **IO密集型**(如网络请求):使用`CachedThreadPool`或自定义线程池,通过公式调整参数: $$ \text{corePoolSize} = N \times U \times (1 + \frac{W}{C}) $$ 其中$N$为CPU核数,$U$为目标CPU利用率(0.7~0.8),$W/C$为等待时间与计算时间的比值[^4]。 - **计算密集型**:使用`FixedThreadPool`,核心线程数设为CPU核数+1。 - **混合型任务**:结合队列类型(如`ArrayBlockingQueue`)与动态扩容策略,避免任务堆积。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值