概念
Android 中线程池的真正实现为ThreadPoolExecutor,使用线程池有以下优点
- 线程可以复用,避免重复创建线程和销毁带来的性能消耗
- 能够对创建的线程进行简单的管理
- 可以控制线程池中的最大并发数
执行流程
核心参数
-
corePoolSize
线程池中的核心线程数,默认情况下核心线程即使在空闲状态也会存活
-
maximumPoolSize
线程池中的线程数量最大数
-
keepAliveTime
非核心线程空闲存活时间大小,超过这个时间线程就会被回收
-
unit
空闲存活时间单位
-
workQueue
存放任务的阻塞队列
-
threadFactory
创建线程的工厂
-
ExecutionHandler
线程池的饱和策略,当线程池无法执行新的任务的时候的处理策略
线程池状态
- RUNNING
能接受新的任务,并且也能处理阻塞队列里的任务
- SHUTDOWN
不再接受新的任务,会继续处理阻塞队列中的任务
- STOP
不接受新任务也不出里阻塞队列中的任务,而且会中断正在执行任务的线程
- TIDYING
所有任务已完成,有效线程数为0
- TERMINATED
执行teminated 方法之后进入该状态
任务阻塞队列
-
ArrayBlockingQueue
基于数组实现的有界阻塞队列,一把锁,两个Condition,
-
LinkBlockingQueue
基于链表的阻塞队列,头尾一把锁,两个Condition,newFixedThreadPool和newSingleThreadExecutor里面用的就是这种
-
DelayQueue
DelayQueue 是一个支持延时获取元素的阻塞队列, 内部采用优先队列 PriorityQueue 存储元素,同时 元素必须实现 Delayed 接口;在创建元素时可以指定多久才可以从队列中获取当前元素,只有在延迟期满时才能从队列中提取元素
-
PriorityBlockingQueue
优先级队列是一个基于堆的无界并发安全的优先级队列
-
SynchronousQueue
synchronousQueue是一个没有数据缓冲的阻塞队列,生产者线程对其的插入操作put()必须等待消费者的移除操作take(),反过来也一样。在cachedThreadPool的任务队列中用到该队列
拒绝策略
四种拒绝策略都是实现这个接口RejectedExecutionHandler 如果添加进来一个任务,线程池的任务数大于最大任务队列和最大线程数的总和,则会调用设置的handler来处理添加进来的任务。
- AbortPolicy
直接拒绝执行且抛出异常
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
throw new RejectedExecutionException("Task " + r.toString() +
" rejected from " +
e.toString());
}
-
DiscardPolicy
只拒绝执行
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
}
-
DiscardOldestPolicy
丢弃最旧的任务然后执行新的任务
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
if (!e.isShutdown()) {
e.getQueue().poll();
e.execute(r);
}
}
-
CallerRunsPolicy
由调用者的线程执行该任务
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
if (!e.isShutdown()) {
r.run();
}
}
类型及使用场景
- newFixThreadPool
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
一种线程数量不变以及只有核心线程的线程池,可以快速响应外界的请求
- newCacheThreadPool
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
这个是只有非核心线程,并且最大线程数为Integer.MAX_VALUE,空闲时长60s,适合执行大量的且耗时较少的任务
- newSingleThreadExecutor
public static ExecutorService newSingleThreadExecutor(ThreadFactory threadFactory) {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>(),
threadFactory));
}
只有一个核心线程的线程池,确保所有的任务都在一个线程中按顺序执行,任务之间不需要处理线程同步的问题
- newScheduledThreadPool
public static ScheduledExecutorService newScheduledThreadPool(
int corePoolSize, ThreadFactory threadFactory) {
return new ScheduledThreadPoolExecutor(corePoolSize, threadFactory);
}
这个是有固定的核心线程而且最大线程数为Integer.MAX_VALUE, 适合执行定时任务和具有固定周期的重复任务
- newWorkStealingThreadPool
public static ExecutorService newWorkStealingPool(int parallelism) {
return new ForkJoinPool
(parallelism,
ForkJoinPool.defaultForkJoinWorkerThreadFactory,
null, true);
}
线程池运行时异常处理
- 直接在run方法中try-catch
- Submit 方法获取future 对象,try-catch future.get() 方法
- 重写ThreadPoolExecutor的afterExecute方法,处理传递的异常引用
- 为工作者线程设置UncaughtExceptionHandler,在uncaughtException方法中处理异常
使用过程需要注意的地方
- FixedThreadPool和SingleThreadPool: 允许的请求队列长度为 Integer.MAX_VALUE,避免堆积大量的请求,从而导致 OOM
- CachedThreadPool: 允许的创建线程数量为 Integer.MAX_VALUE,创建大量的线程,会导致 OOM。
- 实际应用更多以ThreadPoolExecutor 提供的构造方法去创建线程池,根据实际需求制定核心线程数以及使用有界的工作任务队列。