前言
做Android开发,有时会启动大量的线程,如果不适当的管理,会增加系统的开销,降低程序的性能。而线程池是Android性能优化的一个方案,有诸多好处:
1. 可以重用线程池里的线程,避免线程的创建和销带来的性能开销。
2. 可以控制线程池的最大并发数,避免大量线程之前抢占系统资源而造成阻塞现象。
3. 可以对线程进行管理,比如定时执行某线程或是循环执行等功能。
ThreadPoolExecutor
我们知道Java中有一个Executor这样一个接口。而ExecutorService就是继承了Executor的一个接口,真正实现的是ThreadPoolExecutor。先看下ThreadPoolExecutor的构造方法(比较常用的):
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory) {
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
threadFactory, defaultHandler);
}
参数:
- corePoolSize:核心线程。
- maximumPoolSize:线程池所能容纳的最大线程数。
- keepAliveTime:非核心线程闲置的超时时长,如果超过这个值,非核心线程就会被回收。
- unit:用于指定keepAliveTime的时间单位。
- workQueue:线程池中的任务队列。
threadFactory:为线程池提供新建线程的功能。
这些参数配置在AsyncTask中就有所体现:
private static final String LOG_TAG = "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);
/**
* An {@link Executor} that can be used to execute tasks in parallel.
*/
public static final Executor THREAD_POOL_EXECUTOR
= new ThreadPoolExecutor(CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE,
TimeUnit.SECONDS, sPoolWorkQueue, sThreadFactory);
从AsyncTask的代码,我们可以看出:
- 核心线程=cpu核心数+1
- 核心线程没有超时机制,非核心线程的闲置超时时间为1秒
- 线程池的最大线程数为cpu核心数*2+1
任务队列的容量为128
ThreadPoolExecutor执行任务的时候,遵循以下规则:
1 如果线程池里的线程数量没有达到核心线程的数量,那么会直接启动一个核心线程来执行任务。
2 如果线程池中的线程数量已经达到或者超过核心线程的数量,那么任务会被插入到任务队列中的排队等待执行。
3 如果在步骤2中无法将任务插入到任务队列中,这往往是由于任务队列已满,这个时候如果线程数量未达到线程池规定的最大值,那么会立刻启动一个非核心线程来来执行。
4 如果步骤3中线程数量已经达到线程规定的最大值,那么就拒绝执行此任务,ThreadPoolExecutor 就会调用RejecteExecutionHandler的rejectedExcution方法来通知调用者。
Android中的线程池
Android中也通过配置ThreadPoolExecutor来实现了不同类型的线程池,下面我们来介绍最常用的4类线程池:
1.FixedThreadPool
通过调用Executes的newFixedThreadPool方法来创建:
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
我们发现,FixedThreadPool线程池具备以下特性:
- 线程数量固定,在创建的时候,指定数量。
- 当线程处于空闲状态时,它们不会被回收,直到线程池被关闭。
- 当所有的线程都处于活动状态,新任务会处于等待状态,直到有线程空闲出来。
- 所有线程都是核心线程,且没有超时机制,队列也没有大小限制
2.CachedThreadPool
通过调用Executes的newCachedThreadPool方法来创建:
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
我们发现,CachedThreadPool线程池具备以下特性:
- 线程数量不固定,并且最大数为Integer.MAX_VALUE
- 当线程处于空闲状态时,如果闲置60秒就会被回收
- 当所有的线程都处于活动状态,新任务会创建新的线程执行,否则就使用闲置线程。
- 由此看来,CachedThreadPool线程池适合做大量的耗时较少的任务。
3.ScheduledThreadPool
通过调用Executes的newScheduledThreadPool方法来创建:
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
return new ScheduledThreadPoolExecutor(corePoolSize);
}
public ScheduledThreadPoolExecutor(int corePoolSize) {
super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
new DelayedWorkQueue());
}
我们发现,ScheduledThreadPool线程池具备以下特性:
- 核心线程是固定的,创建时指定
- 非核心线程是不固定的,闲置会被回收
- CachedThreadPool,主要用于执行定时任务和具有固定周期的重复任务
3.SingleThreadExecutor
通过调用Executes的newSingleThreadExecutor方法来创建:
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}
我们发现,SingleThreadExecutor线程池具备以下特性:
- 内部只有一个核心线程
- 所有任务在线程中排队执行
- 感觉作用就是不用担心线程同步了,,