请尊重作者劳动成果,转载请标明原文链接:http://blog.csdn.net/capzhaot/article/details/51727555
1. 使用线程池的优势
在安卓开发中,开启子线程使我们经常接触到的,安卓系统为了App能够流畅的运行,把主线程作为UI-Thread,也就是关于UI绘制相关的动作都必须在主线程中执行,因此,一些耗时操作,比如访问网络,文件操作等等就必须放到子线程中去执行,这就保证了App的页面的流畅性不受耗时操作的影响.
1.1 传统方式开启子线程的缺点:
在开启一个异步任务时,我们最常见的一个写法就是直接new Thread,比如:
new Thread(new Runnable() {
@Override
public void run() {
// task
}
}).start();
这么写的确很方便,但是缺点也很明显:
- 由于采用匿名的方式,导致该子线程无法被管理.比如停止,查看异步任务的进度等等.
- 如果需要大量执行异步任务,子线程就会被频繁的创建,销毁,导致系统资源消耗增加.
1.2 使用线程池的好处:
- 可以使得线程池中的线程重用,避免因为线程频繁的创建和销毁带来的性能开销.
- 能有效控制线程池的最大并发数,避免大量的线程之间相互抢占系统资源而导致的阻塞.
- 能够对线程进行简单的管理,并提供定时执行以及制定间隔循环等功能.
2. Android中线程池的实现:
Android中线程池的概念来源于Java,核心实现为ThreadPoolExecutor类,该类提供了一系列参数来配置线程池,根据其配置的参数不同,Android中线程池主要分为四类,以实现不同特点的线程池.下面我们先来认识一下ThreadPoolExecutor类,了解其参数如何配置,然后再详细介绍四类特点各异的线程池.
2.1 ThreadPoolExecutor类介绍:
首先看一下其构造函数:
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler)
corePoolSize
这个参数表示线程池的核心线程数,核心线程即使空闲也会一直保持存活状态,除非你设置了allowCoreThreadTimeOut(核心线程超时属性)为true.此时闲下来的核心线程一旦超过了keepAliveTime指定的时间,核心线程就会被终止.
maximumPoolSize
表示线程池能容纳的最大线程数量,当池中线程达到此数量时,后续的任务将会被阻塞.
keepAliveTime
当池中线程数量超过核心线程数量时,这些多出的线程如果空闲的话,它的存活时间就为keepAliveTime.超过这个时间,该线程就会被终止.
当allowCoreThreadTimeOut属性设定后,keepAliveTime也会作用于核心线程.
unit
指定keepAliveTime的时间单位,常见的有TimeUnit.MILLISECONDS(毫秒),TimeUnit.SECONDS(秒)以及TimeUnit.MINUTES(分钟)等.
workQueue
指定线程池的任务队列,提交的线程会在此队列中排队.
threadFactory
线程工厂,为线程池的任务创建新的线程,他只有一个方法: Thread newThread(Runnable r).
handler
当线程池已满或者无法继续执行新的任务时,这个时候ThreadPoolExecutor会调用handler的rejectedEcecutor方法来通知调用者.这个参数不太常用.
ThreadPoolExecutor线程创建规则:
(1) 如果线程池的中线程数量未达到核心线程的数量,那么会直接启动一个核心线程来执行任务.
(2)如果线程池中的线程数量达到或者超过核心线程的数量,那么任务会被插入到任务队列中排队等待执行.
(3)如果任务无法被插入到任务队列中,这往往是因为队列已满,若此时线程数量未超过线程池规定的最大值,那么会立即启动一个非核心线程来执行任务.
(4)如果池中数量以达到规定的最大值,那么就拒绝执行任务,这个时候ThreadPoolExecutor会调用rejectedEcecution方法来通知调用者.
3. 线程池的分类:
通过对ThreadPoolExecutor参数的不同配置,可以实现Android中四种常见的具有不同功能的线程池,这四类线程池分别是FixedThreadPool,CacheThreadPool,SckeduleThreadPool,以及SingleThreadExecutor.他们可分别通过Executors类中的对应方法来创建.
1. FixedThreadPool(线程数量固定的线程池)
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
Features:
- 最大线程数与核心线程数相同,因此该线程池只有核心线程.
- keepAliveTime为0,说明核心线程没有超时机制,会一直保持active状态.
- 任务队列使用LinkedBlockingQueue,表示任务队列的大小没有限制.
通过对ThreadPoolExecutor的配置我们发现,FixedThreadPool线程池只有核心线程(就像一架飞机,只有商务舱),即使当线程处于空闲状态也不会被回收,除非线程池关闭. 当核心线程数量满时,新的任务就会处于等待状态,直到有核心线程空闲下来.
优势:
由于FixedThreadPool只有核心线程并且不会被回收,这意味着它能更快速度响应外界请求.
2. CacheThreadPool(线程数量不定的线程池 )
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
Features:
- 没有核心线程,只有非核心线程,并且最大线程数为任意大.(一架全部都是经济舱的飞机…)
- 有超时机制,空闲的线程超过60s就会被回收.
- 当线程池的线程都处于active状态时,线程池会创建新的线程来处理新的任务.
优势:
CacheThreadPool的SynchronousQueue队列是一个非常特殊的队列,基本可以理解为一个无法插入元素的队列.因此,这将导致任何任务都会被立即执行,从这点看,这类线程比较适合执行大量的耗时较少的任务.而当整个线程池都处于闲置状态时,所有的线程都因超时被停止,因此占用系统资源极少.
3. SckeduleThreadPool( 可调度线程池)
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
return new ScheduledThreadPoolExecutor(corePoolSize);
}
public ScheduledThreadPoolExecutor(int corePoolSize) {
super(corePoolSize, Integer.MAX_VALUE,
DEFAULT_KEEPALIVE_MILLIS, MILLISECONDS,
new DelayedWorkQueue());
}
//super方法
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue) {
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
Executors.defaultThreadFactory(), defaultHandler);
}
Features:
- 核心线程数量固定,非核心线程数量无限制.
由于使用的是DelayedWorkQueue,这类线程池主要用于执行定时任务和具有固定周期的重复任务.
4. SingleThreadExecutor( 单核心线程池)
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}
Features:
- 内部只有一个核心线程,没有非核心线程,确保所有任务都在同一个线程中按顺序执行.
- SingleThreadExecutor的意义是统一外界所有的线程到一个线程中,保证其顺序性,这使得其不需要处理线程同步的问题.
4. 四种线程池的简单举例:
Runnable r = new Runnable() {
@Override
public void run() {
SystemClock.sleep(1000);
}
};
ExecutorService fixedThreadPool = Executors.newFixedThreadPool(4);
fixedThreadPool.execute(r);
ExecutorService cachedThreadPool = Executors.newCachedThreadPool();
cachedThreadPool.execute(r);
ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(4);
//延时2s后执行
scheduledThreadPool.schedule(r, 2000, TimeUnit.MILLISECONDS);
//延时1s后,每隔1s执行一次.
scheduledThreadPool.scheduleAtFixedRate(r, 1000, 1000, TimeUnit.MILLISECONDS);
ExecutorService singleThreadExecutor = Executors.newSingleThreadExecutor();
singleThreadExecutor.execute(r);