为什么要有线程池
说道为什么要有线程池,就不得说下线程池能给我们带来什么好处,都有什么优点呢?
优点
线程池可以重复利用池中的线程,可以有效的避免创建线程和销毁线程所带来的性能损耗。我们知道正常的多线程都是执行完一个任务就会将当前的线程销毁,然后触发GC的垃圾回收,这样频繁的GC操作,会导致应用程序卡顿。
能有效控制线程池的最大并发数,这样可以避免大量的线程去争夺系统的资源,这样可以避免争夺资源而产生的系统卡顿和阻塞的现象。
能够对线程进行简单的管理操作,意思就是我们可以自定义线程池来满足我们得需要。
Android中的线程池介绍
Android中的线程池是源自于Java中的Executor,然而Executor是一个接口,它的唯一实现类是ThreadPoolExecutor也就是线程池类,接下来我们就看看这个ThreadPoolExecutor为我们提供了怎样的线程操作。
ThreadPoolExecutor
ThreadPoolExecutor是提供了一系列的配置参数来配置线程池,而我们Android中的线程池都是通过自定义ThreadPoolExecutor的参数来达到想要的功能。
ThreadPoolEcecutor的配置参数介绍
ThreadPoolExecutor是线程池的真正实现,它的构造方法提供了一系列参数来配置线程池,接下来我们就来看看这些构造方法中的配置参数。
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler) {
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
Executors.defaultThreadFactory(), handler);
}
corePoolSize :核心线程数
线程池中的核心线程数,默认情况下,核心线程会在线程池中一直存活,即使它们处于空闲的状态下。有一个特例就是线程池中有一个配置参数 allowCoreThreadTimeOut设置为true,那么核心线程池就得去遵守超时策略,在新任务到来之前核心线程池存活时间受keepAliveTime决定。空闲的线程超过这个时间就会被销毁。
maximumPoolSize: 最大线程数
线程池中所能容纳的最大线程数,当线程池中所能容纳的数达到饱和的状态,后续的任务就会被阻塞。
keepAliveTime
针对非核心线程来说,当处于空闲的状态时候,如果超过这个时间,非核心线程就会被回收。针对核心线程来说,当参数allowCoreThreadTimeOut设置为true,keepAliveTime同样适用于空闲的核心线程。
unit:时间单位
是用于指定keeoAliveTime的单位。
workQueue:任务队列
线程池通过execute方法提交的Runnable对象都会存储在这个任务队列中。
threadFactory :线程工厂
线程池通过线程工厂来创建新的线程,ThreadFactory是一个接口,只有一个newThread(Rnnnable r)方法。
RejectedExecutionHandler:饱和策略
当线程池的数量和任务队列的数量都满的情况,所应对的策略,默认是AbordPolicy,表示无法处理新的任务,并抛出RejectedExecutionException异常。
ThreadPoolExecutor 执行任务遵守的规则
如果线程池中的线程数量没有超过核心线程数量,那么接下来的要执行的任务,会直接启动一个核心线程中执行该任务。
如果线程池中的线程数量超过核心线程数,那么接来的任务会被插入到任务队列中排队等待。
如果当线程中的数量超过核心线程数,并且无法将任务插入到任务队列中,这往往是由于任务队列已经满了,这个时候如果线程数没有达到最大线程数,最自动启动一个非核心线程去执行该任务。
如果任务队列已满 并且线程数应达到最大的线程数。那么就拒绝执行此任务,ThreadPoolExecutor会调用RejectedExecutionHandler的rejectExcution方法来通知调用者。
下面用一张图总结一下上面遵守的执行规则
线程池执行的原理图.png
Android中线程池的分类
接下来介绍的几种线程池都是通过ThreadPoolExecutor配置参数演化而来的,来实现特定的功能。
FixedThreadPool(定长线程池)
构造方法分析
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue());
}
FixedThreadPool 是通过newFixedThreadPool方法创建的,内部看源码得知 方法内部通过new创建了一个FixedThreadPool,其中传入了参数,通过参数分析得知,这个线程池中有固定的线程数,并且只有核心线程存在,并且核心线程不会被回收,当所有的线程都是处于工作的时候,新的任务会存入在任务队列中,直到有核心线程空余的时候,会去执行任务队列中的任务。
CachedThreadPool(可缓存线程池)
构造方法的分析
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue());
}
CachedThreadPool 是通过 newCachedThreadPool方法得到的,看构造方法入参分析得知,这个线程池中没有核心线程池,线程池中最大线程数为Integer.MAX_VALUE意味的可以容纳无限数量的非核心线程,非核心线程的超时策略为60s,当空闲的非核心线程超过60s就会被回收。SynchronousQueue其实是一个空队列进入的任务会被立马执行,不是处于空闲的线程或者就是创建新的线程去执行,CachedThreadPool适合用于大量的耗时少的任务上。
这里特殊说明下CachedThreadPool执行原理,首先执行execute()方法,此时SynChronousQueue队列会执行offer方法,来提交任务,然后同时查看线程池中是否有空闲线程在执行SynChronousQueue的poll方法来移除任务,如果有就相当于配对成功了,如果没有就创建新的非核心线程去队列中取任务,然后执行此任务。
ScheduledThreadPool(定时线程池)
构造方法分析
public static ScheduledExecutorService newScheduledThreadPool(
int corePoolSize, ThreadFactory threadFactory) {
return new ScheduledThreadPoolExecutor(corePoolSize, threadFactory);
}
public ScheduledThreadPoolExecutor(int corePoolSize,
ThreadFactory threadFactory) {
super(corePoolSize, Integer.MAX_VALUE,
DEFAULT_KEEPALIVE_MILLIS, MILLISECONDS,
new DelayedWorkQueue(), threadFactory);
}
ScheduledThreadPool是通过newScheduledThreadPool方法来得到的,我们通过入参的参数分析得知,这是一个固定的核心线程的线程池,非核心线程是无限大的,当非核心线程空闲超过10毫秒(事件极短,相当于立即回收)回进行回收。该线程池适合用于指定定时任务和具有周期的重复任务。
SingleThreadExecutor(单线程线程池)
构造方法分析
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue()));
}
SingleThreadExecutor是通过方法newSingleThreadExecutor方法得到的,通过分析构造方法的参数得知,只有一个核心线程,最大线程数为1,不会创建非核心线程,这样能顺序执行任务队列中的任务,这个线程能控制并发数,避免了大量线程争夺资源而产生的问题。
总结
下面我们从各方面总结和对比下Android中的线程池
Android中的线程池.png