线程是操作系统调度的最小单元,同时线程又是一种受限的系统资源,即线程不可能无限制的产生,并且线程的创建和销毁都会有相应的开销。当系统中存在大量的线程时,系统会通过时间片轮转的方式调度每个线程,因此线程不可能做到绝对的并行,除非CPU核心数>线程数,一般来说是不可能的。试想一下如果在一个进程中频繁的创建和销毁线程,显然不是高效的做法。正确的方法是使用线程池,一个线程池会缓存一定数量的线程,做到线程重用、有效控制最大并发数,避免因为频繁创建和销毁线程带来的系统开销。
如何创建并配置线程池:
线程池的概念来源于Java的Executor,Executor是一个接口,真正的线程池的实现为ThreadPoolExecutor。它的构造方法提供了一系列参数来配置线程池:
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler)
corePoolSize:指线程池的核心线程数,默认情况下核心线程会在线程池中一直存活,即时它们处于闲置状态。但如果将ThreadPoolExecutor的allowCoreThreadTimeOut属性置为true,那么如果核心线程等待的时长超过keepAliveTime,核心线程就会被终止。
maximumPoolSize:线程池所能容纳的最大线程数,当活动线程达到这个数值后,后续的新任务将会被阻塞。
keepAliveTime:非核心线程闲置时的超时时长,超过时长就会被回收。allowCoreThreadTimeOut属性置为true,那么这个属性对核心线程亦有效。
unit:keepAliveTime的时间单位,如TimeUnit.MILLISECONDS(毫秒)。
workQueue:线程池中的任务队列,通过线程池的execute方法提交的Runnable对象会存储在这里。
threadFactory:线程工厂,为线程池创建新线程。ThreadFactory只有一个方法:Thread newThread(Runnable r)。
RejectedExecutionHandler:当线程池无法执行新任务时,可能是由于任务队列已满或无法成功执行任务,这时ThreadPoolExecutor会调用RejectedExecutionHandler的rejectedExecution方法,默认rejectedExecution会直接抛出一个RejectedExecutionException(走AbortPolicy的实现)。
ThreadPoolExecutor执行任务的规则:
如果线程池中的线程数量未达到核心线程的数量,那么会直接创建一个核心线程执行任务。(注意核心线程、非核心线程都是线程池创建的,并非外界赋予线程池的,外界只是赋予任务)
如果任务无法插入到任务队列中,往往是因为任务队列已满。这时如果线程数量未达线程池规定的最大值,那么会立刻创建一个非核心线程来执行任务。(注意任务队列的长度跟线程池所能容纳的最大线程数不是一个概念)
如果2中线程数量已达线程池规定的最大值,那么线程池会拒绝执行任务,ThreadPoolExecutor会调用RejectedExecutionHandler的rejectedExecution方法来通知调用者。
AsyncTask内部就是使用了线程池,来看一下AsyncTask是怎样配置线程池的:
private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();
private static final int CORE_POOL_SIZE = CPU_COUNT + 1;
private static final int MAXIMUM_POOL_SIZE = CPU_COUNT * 2 + 1;
private static final int KEEP_ALIVE = 1;
private static final ThreadFactory sThreadFactory = new ThreadFactory() {
private final AtomicInteger mCount = new AtomicInteger(1);
public Thread newThread(Runnable r) {
return new Thread(r, "AsyncTask #" + mCount.getAndIncrement());
}
};
private static final BlockingQueue<Runnable> sPoolWorkQueue =
new LinkedBlockingQueue<Runnable>(128);
public static final Executor THREAD_POOL_EXECUTOR
= new ThreadPoolExecutor(CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE,
TimeUnit.SECONDS, sPoolWorkQueue, sThreadFactory);
可见AsyncTask配置的线程池规格如下:
核心线程数:CPU核心数 + 1
最大线程数:2 * CPU核心数 + 1
非核心线程闲置时间:1秒
任务队列长度:128
线程池的分类
Android中常见的有4类线程池,它们也是通过配置ThreadPoolExecutor来实现自己的功能特性:
1. FixedThreadPool
特点:线程数量固定,只有核心线程,当线程处于空闲状态时不会被回收,任务队列大小无限制。
创建方法:
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
2. CachedThreadPool
特点:只有非核心线程,最大线程数可以任意大,所以任何任务都会被立刻执行,空闲线程超时时长为60秒,适合执行大量的耗时较少的任务。
创建方法:
public static ExecutorService newCachedThreadPool(ThreadFactory threadFactory) {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
3. ScheduledThreadPool
特点:核心线程数量固定,非核心线程数量无限制,非核心线程空闲时会被立刻回收,适合执行定时任务和具有固定周期的重复任务。
创建方法:
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
return new ScheduledThreadPoolExecutor(corePoolSize);
}
4. SingleThreadExecutor
特点:内部只有一个核心线程,能确保所有任务在同一个线程中顺序执行,任务之间不需处理线程同步的问题。
创建方法:
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}
使用示例:
Runnable runnable = new Runnable() {
@Override
public void run() {
SystemClock.sleep(2000);
}
};
ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(5);
//1秒后执行runnable
scheduledThreadPool.schedule(runnable, 1000, TimeUnit.MILLISECONDS);
//0.5秒后每1秒执行一次runnable
scheduledThreadPool.scheduleAtFixedRate(runnable, 500, 1000, TimeUnit.MILLISECONDS);
ExecutorService singleThreadPool = Executors.newSingleThreadExecutor();
singleThreadPool.execute(runnable);