我吐了,老忘点边边脚脚 好记性不如烂笔头!
线程池的作用
1.重用线程池中的线程,减少线程的创建和销毁带来的开销
2.有效的控制线程的最大并发数,避免大量线程之间因为相互抢占系统资源而导致的阻塞现象。
3.提供简单的管理,定时执行,指定间隔循环执行,线程资源常驻及释放
线程池的配置
Android中线程池的概念来源于java中的Executor,具体实现为 ThreadPoolExecutor。可以通过它的构造参数来创建不同类型的线程池。
构造参数详细
对参数作用充分理解后,方便后面对默认提供的四种线程池的应用场景的分析
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler)复制代码
1.corePoolSize 核心线程最大数量
情况1:allowCoreThreadTimeout = flase(默认),核心线程会伴随线程池的整个生命周期一直存活,即使处于闲置
情况2:allowCoreThreadTimeout = true,闲置的核心线程在等待新任务时可触发超时策略,时间间隔为keepAliveTime
2.maximumPoolSize 线程池能容纳的最大线程数
当活动线程数量达到这个值以后,后续的新任务将会被阻塞(进度阻塞队列,等待绑定执行)
3.keepAliveTime 非核心线程闲置的超时时长(在1中情况2时也会作用于核心线程)
闲置线程等待任务超过这个时长,将会被回收。
4.unit keepAliveTime 的单位
可以使用毫秒,秒,分钟
5.workQueue
线程池的任务队列,通过线程池的execute方法提交的Runnable对象会存储在这个参数中。
6.threadFactory
线程工厂,为线程池提供创建新线程的功能的接口。
RejectedExecutionHandler 拒绝执行的回掉
当线程池无法执行新任务时,任务队列已满或是无法成功执行任务。
合理配置参数
参考AsyncTask,但是这并不是一个固定指标。
AsyncTask的线程池配置如下:
核心线程数=CPU核心数+1
线程池最大线程数=CPU核心数量*2+1
核心线程无超时,非核心线程闲置超时时间=1s
任务队列容量为128
复制代码
ThreadPoolExecutor 线程创建规则
1.如果线程池中的线程数量未达到核心线程的数量,则直接启动一个核心线程来执行任务。
2.如果线程池中的数量>=核心线程数量,那么任务将会被插入任务队列中,排队等待执行
3.如果2中无法将任务插入到任务队列中(正常情况下任务队列已满),此时如果线程总数(核心+非核心)<=线程池配置的最大线程数,则启动一个非核心线程来执行任务
4.如果3中线程总数量已达到线程池配置的最大线程数量,则拒绝执行此任务,回调RejectedExecutionHandler的rejectedExecution方法。
线程池的分类应用
SDK提供了4中常用线程池,分别适应不同的应用场景。FixedThreadPool,CachedThreadPool,ScheduledThreadPool,SingleThreadExecutor
FixedThreadPool 核心版
public static ExecutorService newFixedThreadPool(int nThreads){
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue());
}
复制代码
核心线程数量=最大线程数量,超时时间=0。所以这个线程池只有核心线程,而且核心线程不会超时。一个吞吐量固定,长期营业的线程池。
优点:响应任务的速度快,任务量与吞吐量饱和时,任务处理效率最大化。
缺点:并发能力较弱,吞吐量>任务数量时不可避免会造成资源浪费(主要是内存)。
应用场景:处理需要长期快速响应,无很高的并发效率要求。对批量任务执行无较高的时间等待要求。
CachedThreadPool 裸奔版本
public static ExecutorService newCachedThreadPool(ThreadFactory threadFactory){
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue(),
threadFactory);
}
复制代码
没有核心线程,只有非核心线程 数量=Integer.MAX_VALUE。超时时间60s,无缓存任务队列(SynchronousQueue:简单理解为一个无法存储元素的队列)
优点:所有任务都会被立即分配线程执行,几乎可以理解为一个突破手。用于处理集中并发任务。在全部线程都超时后,这个线程池几乎是不占任何系统资源的(我将这里类比为主动new N个普通线程)
缺点:随着任务数量激增可能会导致系统资源匮乏,导致线程阻塞。
应用场景:处理大量耗时较少的任务
ScheduledThreadPool 调度版
public ScheduledThreadPoolExecutor(int corePoolSize,
ThreadFactory threadFactory){
super(corePoolSize, Integer.MAX_VALUE,
DEFAULT_KEEPALIVE_MILLIS, MILLISECONDS,
new DelayedWorkQueue(), threadFactory);
}
public ScheduledFuture> schedule(Runnable command,
long delay, TimeUnit unit);
复制代码
非核心线程数量Integer.MAX_VALUE,闲置线程等待超时10s。
优点:非核心线程10s就会被即时回收,释放速度快,吞吐量大于FixedThreadPool
响应速度略高于CachedThreadPool。
缺点:这是一个非极端的线程池,缺点不明显
应用场景:使用schedule()方法处理定时任务,或处理固定周期的重复任务,执行完后直到下次执行期间,尽量少的占用资源。可以参考这个配置自定义线程池,更好的适应具体场景,比如将DEFAULT_KEEPALIVE_MILLIS 设置为0s,加速资源释放。
SingleThreadExecutor 单实例版
public static ExecutorService newSingleThreadExecutor(ThreadFactory threadFactory){
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue(),
threadFactory));
}
复制代码
就一个核心线程,没有非核心线程,队列使用LinkedBlockingQueue。毫无疑问这是个任务队列+单线程的顺序执行的“任务池”。
应用场景:处理需要按顺序执行的任务。
END