一、为什么需要线程池?
在 Android 开发中,多线程是异步任务处理的基石。但直接创建线程存在三大痛点:
- 资源浪费:频繁创建/销毁线程消耗 CPU 和内存。
- 管理失控:线程数量爆炸式增长易导致 OOM 和 ANR。
- 调度低效:缺乏统一的任务队列和拒绝策略机制。
线程池的价值:
✅ 线程复用:避免重复创建开销
✅ 队列管理:支持优先级和流量控制
✅ 统一策略:提供异常处理和监控能力
二、线程池核心实现:ThreadPoolExecutor
ThreadPoolExecutor
是线程池的核心实现类,通过 7 个关键参数控制行为:
public ThreadPoolExecutor(
int corePoolSize, // 核心线程数(常驻线程)
int maximumPoolSize, // 最大线程数(含核心与非核心)
long keepAliveTime, // 非核心线程闲置存活时间
TimeUnit unit, // 时间单位
BlockingQueue<Runnable> workQueue, // 任务队列
ThreadFactory threadFactory, // 线程工厂(定制线程名/优先级)
RejectedExecutionHandler handler // 饱和策略
)
执行流程(附流程图)
线程池执行流程图
- 任务提交时,优先使用核心线程
- 核心线程满则入队
- 队列满则扩容至最大线程数
- 仍满则触发拒绝策略
饱和策略对比
策略 | 行为 | 适用场景 |
---|---|---|
AbortPolicy | 抛 RejectedExecutionException | 严格任务完整性要求 |
CallerRunsPolicy | 用调用者线程执行 | 不允许任务丢失 |
DiscardPolicy | 静默丢弃 | 可容忍任务丢失 |
DiscardOldestPolicy | 丢弃队列最旧任务并重试 | 最新任务优先级高 |
三、默认线程池对比与选择
通过 Executors
提供的四种预设线程池:
类型 | 核心参数 | 优点 | 缺点 | 适用场景 |
---|---|---|---|---|
FixedThreadPool | 固定线程数 + 无界队列 | 严格并发控制 | 易 OOM | CPU 密集型任务 |
CachedThreadPool | 0核心 + MAX_VALUE线程 + 直接提交队列 | 自动扩缩容 | 线程数失控 | 短期突发任务 |
SingleThreadPool | 单线程 + 无界队列 | 天然线程安全 | 性能瓶颈 | 顺序执行任务 |
ScheduledPool | 自定义核心数 + 延迟队列 | 精准定时控制 | 异常导致任务中断 | 心跳检测/定时任务 |
示例代码:
// 创建 CachedThreadPool(不推荐直接使用)
ExecutorService cachedPool = Executors.newCachedThreadPool();
// 等效 ThreadPoolExecutor
new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS,
new SynchronousQueue<>());
四、项目级线程收敛方案
在大型项目中,需解决 线程资源分散 和 第三方库不可控 问题。完整方案如下:
1. 线程池分类管理
类型 | 配置参数 | 适用场景 |
---|---|---|
IO 池 | core=CPU*2, queue=100 | 网络请求/文件读写 |
CPU 池 | core=CPU, queue=50 | 数据计算/加密 |
低优先级池 | core=2, queue=200 | 日志上传/后台任务 |
定时池 | core=3, 延迟队列 | 定时任务 |
2. 统一线程池管理器
public class ThreadPoolManager {
// IO 密集型池
private static ThreadPoolExecutor ioExecutor = new ThreadPoolExecutor(
4, 8, 30, TimeUnit.SECONDS,
new LinkedBlockingQueue<>(100),
new NamedThreadFactory("IO-Thread"),
new DiscardOldestPolicy()
);
// CPU 密集型池
private static ThreadPoolExecutor cpuExecutor = new ThreadPoolExecutor(
Runtime.getRuntime().availableProcessors(),
Runtime.getRuntime().availableProcessors() * 2,
60, TimeUnit.SECONDS,
new LinkedBlockingQueue<>(50),
new NamedThreadFactory("CPU-Thread"),
new AbortPolicy()
);
// 获取执行器(带类型路由)
public static Executor getExecutor(TaskType type) {
switch (type) {
case IO: return ioExecutor;
case CPU: return cpuExecutor;
default: return ioExecutor;
}
}
}
3. 监控与告警
// 监控指标示例
String status = String.format(
"PoolSize=%d, Active=%d, Queue=%d",
executor.getPoolSize(),
executor.getActiveCount(),
executor.getQueue().size()
);
// 异常捕获
Thread.setDefaultUncaughtExceptionHandler((thread, ex) -> {
Log.e("ThreadPool", "线程崩溃: " + thread.getName(), ex);
CrashReport.logException(ex);
});
4. 代码规范约束
-
Lint 检查规则:禁止直接使用
new Thread()
和Executors
- 自定义注解:强制使用统一线程池
@Retention(RetentionPolicy.SOURCE)
@Target(ElementType.METHOD)
public @interface UseGlobalExecutor {}
五、高阶实践:Gradle 插件动态替换线程池
通过 字节码插桩 实现自动化线程收敛,覆盖第三方库:
1. 插件实现原理
plugin.jpg
2. 核心代码示例
// 自定义 Transform
class ThreadPoolTransform extends Transform {
void transform(...) {
inputs.each { input ->
input.jarInputs.each { jar ->
// 修改第三方库字节码
processJar(jar.file)
}
}
}
}
// ASM 修改逻辑
public class ThreadPoolClassVisitor extends ClassVisitor {
@Override
public MethodVisitor visitMethod(...) {
return new AdviceAdapter(api, mv, access, name, desc) {
@Override
protected void onMethodEnter() {
// 替换 new Thread() 为线程池调用
if (opcode == NEW && "java/lang/Thread".equals(type)) {
invokeStatic(GLOBAL_THREAD_POOL_METHOD);
}
}
};
}
}
3. 动态配置
// build.gradle 配置
threadPoolPlugin {
enable = true
replaceNewThread = true
excludePackages = ["com.google.android.gms"]
poolConfig {
coreSize = 4
maxSize = 8
}
}
4. 第三方库处理技巧
-
Hook 系统类加载器:替换
Thread
构造函数 -
动态代理:拦截
Runnable.run()
方法 - 字节码替换:修改 OkHttp/Retrofit 内部线程池
六、效果验证与数据
指标 | 收敛前 | 收敛后 | 提升幅度 |
---|---|---|---|
峰值线程数 | 158 | 22 | 86% |
OOM 发生率 | 2次/周 | 0 | 100% |
ANR 率 | 0.15% | 0.03% | 80% |
冷启动时间 | 2.3s | 1.8s | 22% |
七、总结与展望
线程池最佳实践路线图:
-
基础规范:禁用
new Thread()
,使用统一管理器 - 监控体系:建立线程池健康度指标
- 高阶优化:动态参数调整 + 自动化收敛
未来演进方向:
- AI 调参:根据设备性能动态优化线程数
- 无侵入监控:基于字节码的运行时探针
- 跨进程线程池:统一管理多进程线程资源