一.概念浅谈
线程池也就是装线程的池子,它相比于直接创建线程的好处就是在于它能减少在创建和销毁线程上所花的时间以及系统资源的开销,从而解决系统资源不足的问题。在阿里巴巴代码规范中提到——如果不使用线程池,有可能造成系统创建大量同类线程而导致消耗完内存或者“过度切换”的问题(相同优先级的线程会通过系统时间片轮转的方式去执行任务,所以线程之间做不到绝对的并发)。另外创建匿名线程不便于后续的资源使用分析,对性能分析会造成困扰。
二.使用说明
- 在android中官方提供了
AsyncTask - java中见名知意的
ThreadPoolExector
二.AsyncTask浅析
AsyncTask是由两个线程池和一个Handler构成的,闲话不多说,直接看源码
//这是一个任务队列线程池,让需要执行的多个线程任务按顺序排列
private static class SerialExecutor implements Executor {
final ArrayDeque<Runnable> mTasks = new ArrayDeque<Runnable>();
Runnable mActive;
//通过同步锁来保证任务是串行执行的
public synchronized void execute(final Runnable r) {
mTasks.offer(new Runnable() {
public void run() {
try {
r.run();
} finally {
scheduleNext();
}
}
});
if (mActive == null) {
scheduleNext();
}
}
protected synchronized void scheduleNext() {
if ((mActive = mTasks.poll()) != null) {
THREAD_POOL_EXECUTOR.execute(mActive);
}
}
}
//THREAD_POOL_EXECUTOR就是核心的任务线程池
private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();
// We want at least 2 threads and at most 4 threads in the core pool,
// preferring to have 1 less than the CPU count to avoid saturating
// the CPU with background work
private static final int CORE_POOL_SIZE = Math.max(2, Math.min(CPU_COUNT - 1, 4));
private static final int MAXIMUM_POOL_SIZE = CPU_COUNT * 2 + 1;
private static final int KEEP_ALIVE_SECONDS = 30;
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;
static {
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE_SECONDS, TimeUnit.SECONDS,
sPoolWorkQueue, sThreadFactory);
threadPoolExecutor.allowCoreThreadTimeOut(true);
THREAD_POOL_EXECUTOR = threadPoolExecutor;
}
原来它的底层还是通过ThreadPoolExecutor来实现的,这里就给出了一个很好的ThreadPoolExector创建的示范:
-
CORE_POOL_SIZE核心线程数,最低两个最高四个,如果cpu核数-1超过四个就取4,最少要有2个。 -
MAXIMUM_POOL_SIZE线程池中的最大线程数,取CPU_COUNT * 2 + 1 -
KEEP_ALIVE_SECONDS非核心线程,闲置超时时常 30s
三.ThreadPoolExecutor的几种创建方法
//定长线程池 只有核心线程 & 不会被回收、线程数量固定、任务队列无大小限制
ExecutorService executor = Executors.newFixedThreadPool(3);
//定时线程池 核心线程数量固定、非核心线程数量无限制
ExecutorService executor = Executors.newScheduledThreadPool(1);
//可缓存线程池 只有非核心线程、线程数量不固定(可无限大)、灵活回收空闲线程(具备超时机制,全部回收时几乎不占系统资源)、新建线程(无线程可用时)
ExecutorService executor = Executors.newCachedThreadPool();
//单线程化线程池 只有一个核心线程
ExecutorService executor = Executors.newSingleThreadExecutor();
虽然官方给我们提供了四种创建线程池的方式,但是事实上
-
FixedThreadPool和SingleThreadPool: 允 许 的 请 求 队 列 长 度 为 Integer.MAX_VALUE,可能会堆积大量的请求,从而导致OOM; -
CachedThreadPool和ScheduledThreadPool:允许的创建线程数量为 Integer.MAX_VALUE,可能会创建大量的线程,从而导致OOM;
所以在阿里巴巴的编程规范中,推荐使用自定义线程池的方式,这样的处理方式让写的同学更加明确线程池的运行规则,规避资源耗尽的风险。
推荐的创建方式:
int NUMBER_OF_CORES = Runtime.getRuntime().availableProcessors();
int KEEP_ALIVE_TIME = 1;
TimeUnit KEEP_ALIVE_TIME_UNIT = TimeUnit.SECONDS;
BlockingQueue<Runnable> taskQueue = new LinkedBlockingQueue<Runnable>();
ExecutorService executorService = new ThreadPoolExecutor(NUMBER_OF_CORES,
NUMBER_OF_CORES*2, KEEP_ALIVE_TIME, KEEP_ALIVE_TIME_UNIT, taskQueue,
new BackgroundThreadFactory(), new DefaultRejectedExecutionHandler());
//执行任务
executorService.execute(new Runnnable() {
...
});