Android 线程池(ThreadPool)架构与使用详解
一、整体架构总览
css
┌─────────────────────────────────────────────────────────────────┐
│ Executor 框架体系 │
│ │
│ ┌───────────────┐ │
│ │ Executor │ ← 顶层接口: execute(Runnable) │
│ └───────┬───────┘ │
│ │ │
│ ┌───────▼───────────┐ │
│ │ ExecutorService │ ← 扩展: submit/shutdown/invokeAll │
│ └───────┬───────────┘ │
│ │ │
│ ┌───────▼─────────────────┐ │
│ │ AbstractExecutorService │ ← 模板方法实现 │
│ └───────┬─────────────────┘ │
│ │ │
│ ┌───────▼─────────────────┐ ┌───────────────────────┐ │
│ │ ThreadPoolExecutor │ │ ScheduledThreadPool │ │
│ │ (核心实现) │ │ Executor (定时任务) │ │
│ └───────┬─────────────────┘ └───────────────────────┘ │
│ │ │
│ │ 工厂类封装 │
│ ┌───────▼─────────────────────────────────────────────┐ │
│ │ Executors (工厂类) │ │
│ │ ├── newFixedThreadPool() │ │
│ │ ├── newCachedThreadPool() │ │
│ │ ├── newSingleThreadExecutor() │ │
│ │ ├── newScheduledThreadPool() │ │
│ │ └── newWorkStealingPool() (Java 8, ForkJoinPool) │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
│ Android 特有 / 框架使用 │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ AsyncTask.THREAD_POOL_EXECUTOR (已废弃) │ │
│ │ Glide: GlideExecutor (多个专用线程池) │ │
│ │ OkHttp: Dispatcher 内部线程池 │ │
│ │ Room: 查询/写入线程池 │ │
│ │ Kotlin Coroutines: Dispatchers.Default/IO │ │
│ │ WorkManager: Configuration.executor │ │
│ └─────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────┘
二、ThreadPoolExecutor 核心架构
构造函数 --- 7 个关键参数
java
public ThreadPoolExecutor(
int corePoolSize, // 核心线程数(常驻)
int maximumPoolSize, // 最大线程数
long keepAliveTime, // 非核心线程空闲存活时间
TimeUnit unit, // 时间单位
BlockingQueue<Runnable> workQueue, // 任务等待队列
ThreadFactory threadFactory, // 线程工厂
RejectedExecutionHandler handler // 拒绝策略
)
ini
┌────────────────────────────────────────────────────────────────┐
│ ThreadPoolExecutor 内部结构 │
│ │
│ ┌──────────────────────────────────────────────────────────┐ │
│ │ Worker 线程集合 │ │
│ │ │ │
│ │ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ │ │
│ │ │ Worker-1 │ │ Worker-2 │ │ Worker-3 │ ... │ Worker-N │ │ │
│ │ │ (核心) │ │ (核心) │ │ (非核心) │ │ (非核心) │ │ │
│ │ └────┬────┘ └────┬────┘ └────┬────┘ └────┬────┘ │ │
│ │ │ │ │ │ │ │
│ │ └───────────┴─────┬─────┴───────────────┘ │ │
│ │ │ 竞争获取任务 │ │
│ │ ▼ │ │
│ │ ┌─────────────────────┐ │ │
│ │ │ BlockingQueue │ │ │
│ │ │ (任务等待队列) │ │ │
│ │ │ ┌───┬───┬───┬───┐ │ │ │
│ │ │ │T5 │T6 │T7 │T8 │ │ │ │
│ │ │ └───┴───┴───┴───┘ │ │ │
│ │ └─────────────────────┘ │ │
│ └──────────────────────────────────────────────────────────┘ │
│ │
│ corePoolSize = 3 │
│ maximumPoolSize = 5 │
│ keepAliveTime = 60s │
│ workQueue = LinkedBlockingQueue(128) │
│ │
│ ctl (AtomicInteger): 高3位=状态, 低29位=线程数 │
│ 状态: RUNNING → SHUTDOWN → STOP → TIDYING → TERMINATED │
└────────────────────────────────────────────────────────────────┘
任务提交流程 --- 最核心的逻辑
scss
execute(Runnable task)
│
▼
┌───────────────────────────────────┐
│ 当前线程数 < corePoolSize ? │
│ │
│ YES → 创建核心线程执行任务 │ ──→ 直接运行
│ │
│ NO ↓ │
└───────────────────┬───────────────┘
│
▼
┌───────────────────────────────────┐
│ workQueue.offer(task) 入队成功? │
│ │
│ YES → 任务入队等待 │ ──→ 排队等待空闲线程
│ (再次检查线程池状态) │
│ │
│ NO ↓ (队列满) │
└───────────────────┬───────────────┘
│
▼
┌───────────────────────────────────┐
│ 当前线程数 < maximumPoolSize ? │
│ │
│ YES → 创建非核心线程执行任务 │ ──→ 紧急线程处理
│ │
│ NO ↓ (已达最大线程数) │
└───────────────────┬───────────────┘
│
▼
┌───────────────────────────────────┐
│ 执行拒绝策略 │
│ RejectedExecutionHandler.reject() │
└───────────────────────────────────┘
用具体数字举例(core=3, max=5, queue=128):
makefile
任务1-3到来: 创建核心线程 1,2,3 分别执行 → 线程数: 3
任务4-131到来: 进入队列排队 (队列容量128) → 队列: 128个任务
任务132到来: 队列满, 创建非核心线程4 执行 → 线程数: 4
任务133到来: 创建非核心线程5 执行 → 线程数: 5
任务134到来: 线程数=max, 队列满 → 触发拒绝策略 → 抛异常/丢弃/...
Worker 线程工作循环
java
// 简化的 Worker.run() 逻辑
final void runWorker(Worker w) {
Runnable task = w.firstTask; // 创建时分配的第一个任务
w.firstTask = null;
while (task != null || (task = getTask()) != null) {
// 加锁
w.lock();
try {
beforeExecute(thread, task); // 钩子方法
task.run(); // 执行任务!
afterExecute(task, null); // 钩子方法
} finally {
task = null;
w.completedTasks++;
w.unlock();
}
}
// getTask() 返回 null → 线程退出
processWorkerExit(w, completedAbruptly);
}
// getTask(): 从队列获取任务
private Runnable getTask() {
for (;;) {
boolean timed = (当前线程数 > corePoolSize) || allowCoreThreadTimeOut;
Runnable r = timed
? workQueue.poll(keepAliveTime, unit) // 超时获取 → 超时返回null → 线程销毁
: workQueue.take(); // 阻塞获取 → 核心线程永远等待
if (r != null) return r;
// 超时且线程数>core → 返回null → 线程退出循环 → 销毁
}
}
css
线程生命周期:
核心线程:
创建 → 执行任务 → 队列空 → take()阻塞等待 → 新任务来 → 执行 → ... (永远循环)
非核心线程:
创建 → 执行任务 → 队列空 → poll(60s)等待 → 超时 → 返回null → 线程销毁
→ 有任务 → 执行 → ...
三、阻塞队列详解
scss
┌─────────────────────────────────────────────────────────────────┐
│ BlockingQueue 选择 │
├──────────────────┬────────────┬──────────────────────────────────┤
│ 类型 │ 容量 │ 特点 │
├──────────────────┼────────────┼──────────────────────────────────┤
│ LinkedBlockingQueue│ 可选/无界 │ 链表实现,吞吐量高 │
│ │ (默认MAX) │ ⚠️无界时max参数无效,可能OOM │
├──────────────────┼────────────┼──────────────────────────────────┤
│ ArrayBlockingQueue │ 有界(必选) │ 数组实现,公平/非公平锁可选 │
│ │ │ 有界→可触发创建非核心线程 │
├──────────────────┼────────────┼──────────────────────────────────┤
│ SynchronousQueue │ 0 (不存储) │ 直接交接,不缓冲任务 │
│ │ │ 每个任务必须有线程接收 │
│ │ │ CachedThreadPool 使用 │
├──────────────────┼────────────┼──────────────────────────────────┤
│ PriorityBlocking │ 无界 │ 按优先级排序 │
│ Queue │ │ 需实现 Comparable │
├──────────────────┼────────────┼──────────────────────────────────┤
│ DelayQueue │ 无界 │ 延迟获取 │
│ │ │ ScheduledThreadPool 底层使用 │
└──────────────────┴────────────┴──────────────────────────────────┘
队列选择对线程池行为的决定性影响
lua
SynchronousQueue (容量=0):
任务来 → 无法入队 → 必须创建线程 → 线程数快速增长到 max
适合: 大量短任务, CachedThreadPool
ArrayBlockingQueue (有界):
任务来 → 入队 → 队列满 → 创建非核心线程 → 线程数达max → 拒绝
适合: 控制资源使用, 生产环境推荐
LinkedBlockingQueue (无界):
任务来 → 永远能入队 → 非核心线程永远不会创建 → max参数失效
适合: 固定线程数场景, FixedThreadPool
⚠️ 风险: 任务积压导致 OOM
四、四种拒绝策略
java
// 当线程数 = max 且队列满时触发
┌────────────────────┬────────────────────────────────────────┐
│ 策略 │ 行为 │
├────────────────────┼────────────────────────────────────────┤
│ AbortPolicy │ 抛出 RejectedExecutionException │
│ (默认) │ 调用方感知到失败 │
├────────────────────┼────────────────────────────────────────┤
│ CallerRunsPolicy │ 由提交任务的线程自己执行 │
│ │ 不丢弃任务,但会阻塞调用方 │
│ │ 形成天然的反压(back-pressure) │
├────────────────────┼────────────────────────────────────────┤
│ DiscardPolicy │ 静默丢弃新提交的任务 │
│ │ 无任何通知 (危险) │
├────────────────────┼────────────────────────────────────────┤
│ DiscardOldestPolicy│ 丢弃队列中最老的任务,然后重新提交新任务 │
│ │ 适合只关心最新数据的场景 │
└────────────────────┴────────────────────────────────────────┘
java
// 自定义拒绝策略 (Android 推荐做法)
RejectedExecutionHandler handler = (runnable, executor) -> {
// 方案1: 记录日志 + 降级
Log.e("ThreadPool", "任务被拒绝: " + runnable.toString());
reportToMonitor(runnable);
// 方案2: 放入备用队列
backupQueue.offer(runnable);
// 方案3: 在新线程中执行 (保证执行但不受池管理)
if (!executor.isShutdown()) {
new Thread(runnable, "backup-thread").start();
}
};
五、Executors 工厂类 --- 四种预设线程池
1. FixedThreadPool(固定大小)
java
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(
nThreads, // core = max
nThreads, // 固定线程数
0L, TimeUnit.MILLISECONDS, // 不超时(核心线程不销毁)
new LinkedBlockingQueue<Runnable>() // ⚠️ 无界队列
);
}
ini
┌──────────────────────────────────────────────────┐
│ FixedThreadPool(4) │
│ │
│ 线程: [T1] [T2] [T3] [T4] ← 始终4个 │
│ ↑ │
│ 队列: [任务5][任务6][任务7]... ← 无界,永远排队 │
│ │
│ 特点: 线程数固定,任务排队 │
│ 优点: 资源可控,适合CPU密集型 │
│ 缺点: 无界队列,任务积压可能OOM │
│ 适用: 已知并发量的后台任务 │
└──────────────────────────────────────────────────┘
2. CachedThreadPool(弹性大小)
java
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(
0, // core = 0
Integer.MAX_VALUE, // max = 无上限
60L, TimeUnit.SECONDS, // 空闲60s销毁
new SynchronousQueue<Runnable>() // 不缓冲,直接交接
);
}
ini
┌──────────────────────────────────────────────────┐
│ CachedThreadPool │
│ │
│ 初始: 无线程 │
│ 任务来: 无空闲线程 → 创建新线程 │
│ 任务来: 有空闲线程 → 复用 │
│ 空闲60s: 线程销毁 │
│ │
│ 时刻1: [T1忙] [T2忙] [T3忙] │
│ 时刻2: [T1忙] [T2闲] [T3闲] [T4忙] │
│ 时刻3: [T1忙] [T2销毁] [T3销毁] [T4闲] │
│ │
│ 特点: 来多少任务开多少线程 │
│ 优点: 弹性伸缩,短任务响应快 │
│ 缺点: ⚠️ 并发量大时线程数爆炸,OOM │
│ 适用: 大量短生命周期任务 (OkHttp Dispatcher) │
└──────────────────────────────────────────────────┘
3. SingleThreadExecutor(单线程)
java
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService(
new ThreadPoolExecutor(
1, 1, // 只有1个线程
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>() // 无界队列
)
);
}
ini
┌──────────────────────────────────────────────────┐
│ SingleThreadExecutor │
│ │
│ 线程: [T1] ← 始终1个 │
│ ↑ │
│ 队列: [任务2][任务3][任务4]... ← 严格串行执行 │
│ │
│ 特点: 所有任务串行执行,保证顺序 │
│ 优点: 无并发问题,天然线程安全 │
│ 缺点: 吞吐量低 │
│ 适用: 数据库写入/日志写入/顺序事件处理 │
└──────────────────────────────────────────────────┘
4. ScheduledThreadPool(定时/周期)
java
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
return new ScheduledThreadPoolExecutor(
corePoolSize, // 核心线程数
Integer.MAX_VALUE, // max 无上限
// 内部使用 DelayedWorkQueue (基于堆的优先队列)
);
}
java
ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(2);
// 延迟执行
scheduler.schedule(() -> {
Log.d("Task", "3秒后执行");
}, 3, TimeUnit.SECONDS);
// 固定频率执行 (不考虑任务耗时)
scheduler.scheduleAtFixedRate(() -> {
Log.d("Task", "每5秒执行");
}, 0, 5, TimeUnit.SECONDS);
// |--任务--| |--任务--| |--任务--|
// 0s 2s 5s 7s 10s
// 固定延迟执行 (上次结束后等待固定时间)
scheduler.scheduleWithFixedDelay(() -> {
Log.d("Task", "执行完后等3秒再执行");
}, 0, 3, TimeUnit.SECONDS);
// |--任务--| 3s |--任务--| 3s |--任务--|
// 0s 2s 5s 7s 10s
对比总结
scss
┌────────────────────┬────────┬──────────┬──────────┬──────────────┐
│ │ core │ max │ 队列 │ 适用场景 │
├────────────────────┼────────┼──────────┼──────────┼──────────────┤
│ Fixed(N) │ N │ N │ 无界链表 │ CPU密集型 │
│ Cached │ 0 │ MAX │ 同步队列 │ 大量短I/O任务 │
│ Single │ 1 │ 1 │ 无界链表 │ 串行任务 │
│ Scheduled(N) │ N │ MAX │ 延迟队列 │ 定时/周期任务 │
└────────────────────┴────────┴──────────┴──────────┴──────────────┘
⚠️ 阿里编码规范: 禁止使用 Executors 创建线程池
原因: FixedThreadPool/SingleThreadExecutor 无界队列→OOM
CachedThreadPool 无限线程→OOM
推荐: 手动 new ThreadPoolExecutor(), 明确参数
六、Android 中的实际线程池应用
1. AsyncTask 线程池(已废弃,了解设计)
java
// Android 源码中的线程池定义
private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();
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;
public static final Executor THREAD_POOL_EXECUTOR = new ThreadPoolExecutor(
CORE_POOL_SIZE, // 2~4
MAXIMUM_POOL_SIZE, // CPU*2+1
KEEP_ALIVE_SECONDS, TimeUnit.SECONDS,
new LinkedBlockingQueue<Runnable>(128), // 有界! 128
sThreadFactory
);
// 设计解读:
// core = CPU-1 (留一个核给主线程)
// max = CPU*2+1 (考虑I/O等待时可多开)
// 队列128 (有界, 防止OOM)
2. OkHttp Dispatcher 线程池
java
// OkHttp 内部
public synchronized ExecutorService executorService() {
if (executorService == null) {
executorService = new ThreadPoolExecutor(
0, // core = 0
Integer.MAX_VALUE, // max = 无限
60, TimeUnit.SECONDS, // 60s 回收
new SynchronousQueue<>(), // 同步队列
Util.threadFactory("OkHttp Dispatcher", false)
);
}
return executorService;
}
// 但! OkHttp 在 Dispatcher 层面做了并发控制:
// maxRequests = 64
// maxRequestsPerHost = 5
// 所以实际不会创建无限线程
3. Glide 线程池
java
// Glide 的多线程池策略
public final class GlideExecutor {
// 磁盘缓存线程池 (1个线程, 避免磁盘竞争)
public static GlideExecutor newDiskCacheExecutor() {
return new ThreadPoolExecutor(
1, 1, 0, MILLISECONDS,
new PriorityBlockingQueue<>(),
new DefaultThreadFactory("glide-disk-cache")
);
}
// 网络/源数据加载线程池
public static GlideExecutor newSourceExecutor() {
int bestThreadCount = calculateBestThreadCount(); // 基于CPU核心数
return new ThreadPoolExecutor(
bestThreadCount, bestThreadCount,
0, MILLISECONDS,
new PriorityBlockingQueue<>(),
new DefaultThreadFactory("glide-source")
);
}
// 动画线程池 (GIF 解码)
public static GlideExecutor newAnimationExecutor() {
int bestThreadCount = calculateBestAnimationThreadCount(); // 1-2
return new ThreadPoolExecutor(
bestThreadCount, bestThreadCount, 0, MILLISECONDS,
new PriorityBlockingQueue<>(),
new DefaultThreadFactory("glide-animation")
);
}
}
4. Kotlin Coroutines Dispatchers
kotlin
// Dispatchers.Default → CPU 密集型
// 底层: 共享的 CoroutineScheduler
// 线程数: max(2, CPU核心数)
// 有工作窃取算法
// Dispatchers.IO → I/O 密集型
// 底层: 与 Default 共享 CoroutineScheduler
// 线程数: max(64, CPU核心数)
// 额外的线程专门用于阻塞I/O
// Dispatchers.Main ��� 主线程
// 底层: Handler(Looper.getMainLooper())
// Dispatchers.Unconfined → 不限制
// 在调用方线程启动, 在第一个挂起点后在恢复它的线程继续
// 关系图:
// ┌───────────────────────────────────────────┐
// │ CoroutineScheduler │
// │ ┌──────────────────────────────────────┐ │
// │ │ Worker 线程池 (共享) │ │
// │ │ ┌──┐ ┌──┐ ┌──┐ ... ┌──┐ │ │
// │ │ │W1│ │W2│ │W3│ │Wn│ │ │
// │ │ └──┘ └──┘ └──┘ └──┘ │ │
// │ └──────────────────────────────────────┘ │
// │ ↑ Default 使用 ↑ IO 额外线程│
// │ (CPU核心数个) (最多64个) │
// └───────────────────────────────────────────┘
七、Android 最佳实践 --- 手动创建线程池
1. 全局线程池管理器
java
public class AppExecutors {
private static volatile AppExecutors instance;
// CPU 密集型任务 (计算/解码/排序)
private final ExecutorService cpuExecutor;
// I/O 密集型任务 (网络/数据库/文件)
private final ExecutorService ioExecutor;
// 单线程串行 (数据库写入/日志)
private final ExecutorService singleExecutor;
// 定时任务
private final ScheduledExecutorService scheduledExecutor;
// 主线程
private final Executor mainExecutor;
private AppExecutors() {
int cpuCount = Runtime.getRuntime().availableProcessors();
// CPU 密集型: 核心数+1 (多1个防止某线程偶尔阻塞)
cpuExecutor = new ThreadPoolExecutor(
cpuCount + 1,
cpuCount + 1,
30, TimeUnit.SECONDS,
new LinkedBlockingQueue<>(128),
new NamedThreadFactory("cpu"),
new ThreadPoolExecutor.CallerRunsPolicy() // 拒绝时调用方执行
);
// I/O 密集型: 核心数 * 2 (I/O等待时可让出CPU)
ioExecutor = new ThreadPoolExecutor(
cpuCount * 2,
cpuCount * 2 + 1,
30, TimeUnit.SECONDS,
new ArrayBlockingQueue<>(256), // 有界队列!
new NamedThreadFactory("io"),
new ThreadPoolExecutor.CallerRunsPolicy()
);
// 串行任务
singleExecutor = new ThreadPoolExecutor(
1, 1,
0, TimeUnit.SECONDS,
new LinkedBlockingQueue<>(64),
new NamedThreadFactory("single"),
new ThreadPoolExecutor.CallerRunsPolicy()
);
// 定时任务
scheduledExecutor = new ScheduledThreadPoolExecutor(
2,
new NamedThreadFactory("scheduled")
);
// 主线程
mainExecutor = new MainThreadExecutor();
}
public static AppExecutors getInstance() {
if (instance == null) {
synchronized (AppExecutors.class) {
if (instance == null) {
instance = new AppExecutors();
}
}
}
return instance;
}
public ExecutorService cpu() { return cpuExecutor; }
public ExecutorService io() { return ioExecutor; }
public ExecutorService single() { return singleExecutor; }
public ScheduledExecutorService scheduled() { return scheduledExecutor; }
public Executor main() { return mainExecutor; }
// 主线程 Executor
private static class MainThreadExecutor implements Executor {
private final Handler handler = new Handler(Looper.getMainLooper());
@Override
public void execute(Runnable command) {
handler.post(command);
}
}
// 自定义线程工厂 (命名 + 优先级 + 异常处理)
private static class NamedThreadFactory implements ThreadFactory {
private final AtomicInteger counter = new AtomicInteger(0);
private final String prefix;
NamedThreadFactory(String prefix) {
this.prefix = prefix;
}
@Override
public Thread newThread(Runnable r) {
Thread t = new Thread(r, "app-" + prefix + "-" + counter.getAndIncrement());
t.setDaemon(false);
// Android: 设置线程优先级
t.setPriority(Thread.NORM_PRIORITY);
// 未捕获异常处理
t.setUncaughtExceptionHandler((thread, throwable) -> {
Log.e("ThreadPool", "线程异常: " + thread.getName(), throwable);
// 上报崩溃
CrashReporter.report(throwable);
});
return t;
}
}
// 关闭 (Application.onTerminate 或不需要时)
public void shutdown() {
cpuExecutor.shutdown();
ioExecutor.shutdown();
singleExecutor.shutdown();
scheduledExecutor.shutdown();
}
}
2. 使用示例
java
// I/O 任务: 网络请求
AppExecutors.getInstance().io().execute(() -> {
String result = apiService.fetchData();
// 切回主线程更新 UI
AppExecutors.getInstance().main().execute(() -> {
textView.setText(result);
});
});
// CPU 密集型: 图片处理
AppExecutors.getInstance().cpu().execute(() -> {
Bitmap processed = heavyImageProcessing(bitmap);
AppExecutors.getInstance().main().execute(() -> {
imageView.setImageBitmap(processed);
});
});
// 串行任务: 数据库写入
AppExecutors.getInstance().single().execute(() -> {
database.insert(record1);
database.insert(record2); // 保证顺序
});
// 定时任务: 心跳
AppExecutors.getInstance().scheduled().scheduleAtFixedRate(() -> {
sendHeartbeat();
}, 0, 30, TimeUnit.SECONDS);
// 带返回值的任务
Future<String> future = AppExecutors.getInstance().io().submit(() -> {
return apiService.fetchData();
});
String result = future.get(5, TimeUnit.SECONDS); // 阻塞等待结果
八、线程数量设计原则
ini
┌─────────────────────────────────────────────────────────────┐
│ 线程数计算公式 │
├─────────────────────────────────────────────────────────────┤
│ │
│ CPU 密集型任务 (计算/解码/压缩): │
│ 线程数 = CPU 核心数 + 1 │
│ 理由: 多1个防止偶尔的页面错误/I/O阻塞浪费CPU │
│ │
│ I/O 密集型任务 (网络/数据库/文件): │
│ 线程数 = CPU 核心数 × 2 │
│ 理由: I/O等待时CPU空闲,可以切换到其他线程 │
│ │
│ 更精确的公式 (《Java并发编程实战》): │
│ N_threads = N_cpu × U_cpu × (1 + W/C) │
│ N_cpu = CPU核心数 │
│ U_cpu = 目标CPU使用率 (0~1) │
│ W/C = 等待时间 / 计算时间 │
│ │
│ 示例 (4核, 目标80%利用率, I/O等待是计算的4倍): │
│ N = 4 × 0.8 × (1 + 4) = 16 个线程 │
│ │
├─────────────────────────────────────────────────────────────┤
│ Android 特殊考虑: │
│ - 移动设备 CPU 核心通常 4-8 个 │
│ - 大小核架构 (4大核+4小核) │
│ - 电量敏感: 线程过多→CPU频繁唤醒→耗电 │
│ - 内存有限: 每个线程栈约 1MB │
│ - 建议总线程数控制在 20-30 以内 │
└─────────────────────────────────────────────────────────────┘
九、submit vs execute
java
// execute: 无返回值, 异常直接抛出
executor.execute(() -> {
throw new RuntimeException("crash!");
// → UncaughtExceptionHandler 捕获
// → 线程死亡, 线程池创建新线程替代
});
// submit: 有返回值(Future), 异常被封装
Future<?> future = executor.submit(() -> {
throw new RuntimeException("crash!");
// → 异常不会立即抛出!
// → 线程不会死亡
});
try {
future.get(); // 这里才抛出 ExecutionException
} catch (ExecutionException e) {
Throwable cause = e.getCause(); // 获取原始异常
}
scss
┌────────────┬────────────────┬──────────────────────────────┐
│ │ execute() │ submit() │
├────────────┼────────────────┼──────────────────────────────┤
│ 返回值 │ void │ Future<T> │
│ 异常处理 │ 直接抛出到线程 │ 封装在 Future 中 │
│ │ UncaughtHandler│ get() 时才抛出 │
│ 线程存活 │ 异常→线程死亡 │ 异常→线程存活继续复用 │
│ 适用场景 │ fire-and-forget│ 需要结果/需要感知异常 │
└────────────┴────────────────┴──────────────────────────────┘
⚠️ submit 的坑: 如果不调用 future.get(), 异常被静默吞掉!
解决方案:
java
// 方案1: 包装 Runnable, 捕获异常
executor.submit(() -> {
try {
riskyOperation();
} catch (Exception e) {
Log.e("Pool", "任务异常", e);
// 上报
}
});
// 方案2: 重写 afterExecute 钩子
ThreadPoolExecutor pool = new ThreadPoolExecutor(...) {
@Override
protected void afterExecute(Runnable r, Throwable t) {
super.afterExecute(r, t);
if (t == null && r instanceof Future<?>) {
try {
((Future<?>) r).get();
} catch (ExecutionException e) {
t = e.getCause();
} catch (Exception e) {
t = e;
}
}
if (t != null) {
Log.e("Pool", "任务执行异常", t);
}
}
};
十、线程池监控
关键指标
java
ThreadPoolExecutor pool = (ThreadPoolExecutor) executor;
// 实时状态
int activeCount = pool.getActiveCount(); // 正在执行任务的线程数
int poolSize = pool.getPoolSize(); // 当前线程总数
int queueSize = pool.getQueue().size(); // 队列中等待的任务数
long completedCount = pool.getCompletedTaskCount(); // 已完成任务总数
long taskCount = pool.getTaskCount(); // 提交的任务总数
int largestPoolSize = pool.getLargestPoolSize(); // 历史最大线程数
Log.d("Pool", String.format(
"active=%d, pool=%d, queue=%d, completed=%d, largest=%d",
activeCount, poolSize, queueSize, completedCount, largestPoolSize
));
定时监控
java
// 定期输出线程池状态
ScheduledExecutorService monitor = Executors.newSingleThreadScheduledExecutor();
monitor.scheduleAtFixedRate(() -> {
ThreadPoolExecutor pool = (ThreadPoolExecutor) AppExecutors.getInstance().io();
int queueSize = pool.getQueue().size();
int activeCount = pool.getActiveCount();
// 预警: 队列积压
if (queueSize > 100) {
Log.w("PoolMonitor", "⚠️ IO线程池队列积压: " + queueSize);
reportMetric("thread_pool_queue_backlog", queueSize);
}
// 预警: 线程数异常
if (pool.getPoolSize() > pool.getCorePoolSize() * 2) {
Log.w("PoolMonitor", "⚠️ 线程数异常增长: " + pool.getPoolSize());
}
// 定期上报
reportMetric("thread_pool_active", activeCount);
reportMetric("thread_pool_queue", queueSize);
reportMetric("thread_pool_size", pool.getPoolSize());
}, 0, 10, TimeUnit.SECONDS);
自定义可监控线程池
java
public class MonitoredThreadPoolExecutor extends ThreadPoolExecutor {
private final String name;
private final ConcurrentHashMap<Runnable, Long> startTimes = new ConcurrentHashMap<>();
public MonitoredThreadPoolExecutor(String name, int core, int max,
long keepAlive, TimeUnit unit, BlockingQueue<Runnable> queue) {
super(core, max, keepAlive, unit, queue, new NamedThreadFactory(name));
this.name = name;
}
@Override
protected void beforeExecute(Thread t, Runnable r) {
super.beforeExecute(t, r);
startTimes.put(r, System.nanoTime());
// 设置 Android 线程优先级
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
}
@Override
protected void afterExecute(Runnable r, Throwable t) {
super.afterExecute(r, t);
Long startTime = startTimes.remove(r);
if (startTime != null) {
long elapsed = (System.nanoTime() - startTime) / 1_000_000;
if (elapsed > 1000) { // 超过1秒
Log.w("Pool-" + name, "慢任务: " + elapsed + "ms, task=" + r);
}
}
// 异常处理
if (t != null) {
Log.e("Pool-" + name, "任务异常", t);
}
}
@Override
protected void terminated() {
super.terminated();
Log.i("Pool-" + name, "线程池已终止");
}
}
十一、线程池关闭
java
// shutdown(): 温和关闭
executor.shutdown();
// → 不再接受新任务
// → 已提交的任务(队列中+执行中)继续执行
// → 所有任务完成后线程池终止
// shutdownNow(): 强制关闭
List<Runnable> pending = executor.shutdownNow();
// → 不再接受新任务
// → 尝试中断所有执行中的线程 (interrupt)
// → 返回队列中未执行的任务
// → 不保证执行中的任务能停止
// 推荐的关闭方式 (优雅关闭)
public void gracefulShutdown(ExecutorService executor, int timeoutSec) {
executor.shutdown(); // 先温和关闭
try {
// 等待已有任务完成
if (!executor.awaitTermination(timeoutSec, TimeUnit.SECONDS)) {
Log.w("Pool", "超时未完成, 强制关闭");
executor.shutdownNow(); // 超时则强制
// 再等一会
if (!executor.awaitTermination(5, TimeUnit.SECONDS)) {
Log.e("Pool", "线程池未能终止!");
}
}
} catch (InterruptedException e) {
executor.shutdownNow();
Thread.currentThread().interrupt();
}
}
// Android 中的关闭时机
// Application.onTerminate() ← 模拟器才调用,不可靠
// 推荐: Activity/Service onDestroy 时关闭对应的线程池
// 或: 全局线程池随进程生命周期,不主动关闭
十二、常见陷阱与解决方案
陷阱 1:任务异常导致线程死亡
java
// ❌ 问题: execute 中未捕获的异常会杀死线程
executor.execute(() -> {
int result = 1 / 0; // ArithmeticException
// → 线程死亡, 线程池创建新线程替代
// → 频繁创建销毁,性能差
});
// ✅ 解决: 始终 try-catch
executor.execute(() -> {
try {
int result = 1 / 0;
} catch (Exception e) {
Log.e("Task", "任务执行失败", e);
}
});
陷阱 2:线程池引用 Activity 导致内存泄漏
java
// ❌ 问题: 匿名内部类持有外部 Activity 引用
public class MyActivity extends Activity {
void loadData() {
executor.execute(() -> {
String data = fetchData();
// 隐式持有 MyActivity.this
runOnUiThread(() -> textView.setText(data)); // Activity 已销毁!
});
}
}
// ✅ 解决1: 弱引用
public class MyActivity extends Activity {
void loadData() {
WeakReference<MyActivity> weakRef = new WeakReference<>(this);
executor.execute(() -> {
String data = fetchData();
MyActivity activity = weakRef.get();
if (activity != null && !activity.isDestroyed()) {
activity.runOnUiThread(() -> textView.setText(data));
}
});
}
}
// ✅ 解决2: Lifecycle 感知 (推荐)
// 使用 Kotlin Coroutines + lifecycleScope
lifecycleScope.launch {
val data = withContext(Dispatchers.IO) { fetchData() }
textView.text = data // 自动在 onDestroy 时取消
}
陷阱 3:线程池参数不当
java
// ❌ core=0, max=大, 无界队列 → 只有1个线程工作
new ThreadPoolExecutor(0, 100, 60, SECONDS,
new LinkedBlockingQueue<>()); // 无界队列
// core=0, 任务全部入队, 只创建1个线程消费队列
// max=100 永远触发不了!
// ❌ core=大, 有界队列小 → 频繁拒绝
new ThreadPoolExecutor(2, 2, 0, SECONDS,
new ArrayBlockingQueue<>(1)); // 容量1
// 第4个任务就被拒绝!
// ✅ 合理配置
int cpu = Runtime.getRuntime().availableProcessors();
new ThreadPoolExecutor(
cpu, // core: CPU 核心数
cpu * 2, // max: 2倍
30, SECONDS, // 非核心线程30秒回收
new ArrayBlockingQueue<>(256), // 有界队列
new CallerRunsPolicy() // 满了就调用方执行
);
陷阱 4:多个线程池资源浪费
java
// ❌ 每个模块各自创建线程池
class ModuleA { ExecutorService pool = Executors.newFixedThreadPool(4); }
class ModuleB { ExecutorService pool = Executors.newFixedThreadPool(4); }
class ModuleC { ExecutorService pool = Executors.newFixedThreadPool(4); }
// → 12个常驻线程, 内存浪费, 线程切换开销
// ✅ 全局统一管理 + 按类型区分
// 整个 App 只维护 2-3 个线程池 (CPU / IO / Single)
AppExecutors.getInstance().io().execute(...);
AppExecutors.getInstance().cpu().execute(...);
十三、与 Kotlin Coroutines 对比
kotlin
// 传统线程池方式
AppExecutors.getInstance().io().execute {
val data = api.fetchData() // 后台线程
AppExecutors.getInstance().main().execute {
textView.text = data // 主线程
}
}
// Kotlin Coroutines 方式 (推荐)
lifecycleScope.launch { // 主线程开始
val data = withContext(Dispatchers.IO) { // 自动切到IO线程
api.fetchData()
}
textView.text = data // 自动切回主线程
} // Activity销毁自动取消
vbnet
┌──────────────────┬──────────────────┬──────────────────────┐
│ │ 线程池 │ Coroutines │
├──────────────────┼──────────────────┼──────────────────────┤
│ 线程切换 │ 手动 post/execute │ withContext 自动切换 │
│ 取消 │ Future.cancel() │ Job.cancel() / 自动 │
│ 生命周期感知 │ 需手动管理 │ lifecycleScope 自动 │
│ 异常处理 │ try-catch/回调 │ try-catch + Handler │
│ 嵌套/组合 │ 回调地狱 │ 顺序代码风格 │
│ 并发控制 │ CountDownLatch │ async/await │
│ 底层实现 │ 直接管理线程 │ 基于线程池(Dispatchers) │
│ 学习成本 │ 低 │ 中 │
│ 推荐程度 │ 仍然有效 │ ✅ Android 首选 │
└──────────────────┴──────────────────┴──────────────────────┘
Coroutines 底层仍然使用线程池:
Dispatchers.Default → SharedPool (CPU核心数线程)
Dispatchers.IO → SharedPool (最多64线程)
Dispatchers.Main → Handler(MainLooper)
理解线程池是理解 Coroutines 的基础!
十四、完整架构图
scss
┌─────────────────────────────────────────────────────────────┐
│ Android App 线程架构 │
│ │
│ ┌─────────────┐ │
│ │ Main Thread │ ← UI 渲染 / 事件处理 / 生命周期 │
│ │ (UI Thread) │ Looper + MessageQueue + Handler │
│ └──────┬──────┘ │
│ │ post / runOnUiThread │
│ │ │
│ ┌──────▼──────────────────────────────────────────────┐ │
│ │ AppExecutors (全局线程池管理) │ │
│ │ │ │
│ │ ┌──────────────┐ ┌──────────────┐ ┌───────────┐ │ │
│ │ │ CPU Pool │ │ IO Pool │ │ Single │ │ │
│ │ │ core=CPU+1 │ │ core=CPU×2 │ │ core=1 │ │ │
│ │ │ 图片处理 │ │ 网络/数据库 │ │ 串行写入 │ │ │
│ │ │ JSON解析 │ │ 文件读写 │ │ 日志记录 │ │ │
│ │ │ 数据计算 │ │ SP读写 │ │ │ │ │
│ │ └──────────────┘ └──────────────┘ └───────────┘ │ │
│ │ │ │
│ │ ┌──────────────┐ ┌──────────────────────────────┐ │ │
│ │ │ Scheduled │ │ Main Thread Executor │ │ │
│ │ │ 定时心跳 │ │ Handler(MainLooper).post() │ │ │
│ │ │ 轮询刷新 │ │ 回调切回主线程 │ │ │
│ │ └──────────────┘ └──────────────────────────────┘ │ │
│ └──────────────────────────────────────────────────────┘ │
│ │
│ ┌──────────────────────────────────────────────────────┐ │
│ │ 第三方框架自带线程池 │ │
│ │ OkHttp Dispatcher │ Glide Executors │ │
│ │ Room QueryExecutor │ WorkManager │ │
│ │ Retrofit + RxJava │ Coroutine Dispatchers │ │
│ └──────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────┘
总结
markdown
ThreadPoolExecutor 核心要点:
1. 七参数理解
core / max / keepAlive / queue / factory / handler
→ 它们共同决定了线程池的行为模式
2. 任务提交三步曲
core未满→创建核心线程 → 队列未满→入队 → max未满→创建非核心线程 → 拒绝
3. 队列选择决定行为
SynchronousQueue: 不缓冲,快速创建线程 (Cached)
LinkedBlockingQueue: 无界,max失效 (Fixed/Single)
ArrayBlockingQueue: 有界,推荐生产使用
4. Android 最佳实践
- 全局 2-3 个线程池 (CPU/IO/Single)
- 有界队列 + CallerRunsPolicy
- 自定义 ThreadFactory (命名+优先级+异常处理)
- 新项目首选 Kotlin Coroutines (底层仍是线程池)