Java Android线程池实践指南及高频问题
一、为什么要用线程池?
| 问题 | 说明 |
|---|---|
| 频繁创建/销毁线程开销大 | 线程创建涉及系统调用,消耗时间和资源 |
| 线程数量无限制风险 | 大量线程会导致内存溢出、CPU 过度切换 |
| 难以管理 | 缺乏统一的任务调度、取消、优先级控制 |
线程池的核心价值:复用线程、控制并发数、便于管理。
二、Java 线程池核心类:ThreadPoolExecutor
public ThreadPoolExecutor(
int corePoolSize, // 核心线程数
int maximumPoolSize, // 最大线程数
long keepAliveTime, // 非核心线程存活时间
TimeUnit unit, // 时间单位
BlockingQueue<Runnable> workQueue, // 任务队列
ThreadFactory threadFactory, // 线程工厂
RejectedExecutionHandler handler // 拒绝策略
)2.1 七大核心参数详解
| 参数 | 作用 | 重点 |
|---|---|---|
corePoolSize | 即使空闲也保留的线程数 | 任务提交后先创建到 core 数量 |
maximumPoolSize | 线程池允许的最大线程数 | 队列满后才创建到 max |
keepAliveTime | 非核心线程空闲多久被回收 | 只针对 > corePoolSize 的线程 |
workQueue | 存放待执行任务的阻塞队列 | 高频 |
threadFactory | 创建线程的工厂 | 可自定义线程名、优先级、守护状态 |
handler | 拒绝策略 | 高频 |
2.2 任务提交流程(重点!)
提交任务
↓
当前线程数 < corePoolSize?
├── 是 → 创建新线程执行任务
└── 否 → 任务加入 workQueue
↓
队列是否已满?
├── 否 → 排队等待
└── 是 → 当前线程数 < maximumPoolSize?
├── 是 → 创建临时线程执行任务
└── 否 → 执行拒绝策略⚠️ 关键理解:不是先填满队列再创建线程,而是先创建到 core,再填队列,最后再扩容到 max。
2.3 四种拒绝策略
| 策略 | 行为 | 适用场景 |
|---|---|---|
AbortPolicy(默认) | 直接抛 RejectedExecutionException | 需要快速失败 |
CallerRunsPolicy | 由调用线程(主线程)自己执行 | 降低提交速度,自我保护 |
DiscardPolicy | 静默丢弃任务 | 允许丢任务 |
DiscardOldestPolicy | 丢弃队列最老的任务,重试提交 | 新任务更重要 |
三、任务队列(BlockingQueue)类型
| 队列类型 | 特点 | 典型使用 |
|---|---|---|
SynchronousQueue | 不存储元素,直接移交 | CachedThreadPool,高吞吐 |
LinkedBlockingQueue | 无界队列(默认 Integer.MAX_VALUE) | FixedThreadPool,可能 OOM |
ArrayBlockingQueue | 有界数组队列,需指定容量 | 生产环境推荐,防止 OOM |
PriorityBlockingQueue | 支持优先级排序 | 任务有优先级时 |
DelayedWorkQueue | 延迟执行 | ScheduledThreadPool 内部使用 |
四、Executors 工厂方法(及坑!)
// 1. 固定线程数 ExecutorService fixed = Executors.newFixedThreadPool(10); // 等价于:core=max=10, 无界 LinkedBlockingQueue // ❌ 坑:队列无界,任务堆积可能 OOM // 2. 单线程 ExecutorService single = Executors.newSingleThreadExecutor(); // 等价于:core=max=1, 无界 LinkedBlockingQueue // ❌ 坑:同上 // 3. 可缓存线程池 ExecutorService cached = Executors.newCachedThreadPool(); // 等价于:core=0, max=Integer.MAX_VALUE, SynchronousQueue, 60s 回收 // ❌ 坑:允许创建无限线程,可能 OOM // 4. 定时任务 ScheduledExecutorService scheduled = Executors.newScheduledThreadPool(5); // 等价于:core=5, max=Integer.MAX_VALUE, DelayedWorkQueue // ❌ 坑:max 无限制
⚠️ 阿里巴巴开发手册规定
不允许使用 Executors 创建线程池,必须通过
ThreadPoolExecutor手动创建!
原因:Executors 的便捷方法隐藏了风险参数(无界队列、无限线程数),生产环境极易 OOM。
五、Android 中的线程池实践
5.1 自定义线程池(推荐写法)
public class ThreadPoolManager {
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 MAX_POOL_SIZE = CPU_COUNT * 2 + 1;
private static final long KEEP_ALIVE = 30L;
private static final ThreadPoolExecutor executor = new ThreadPoolExecutor(
CORE_POOL_SIZE,
MAX_POOL_SIZE,
KEEP_ALIVE,
TimeUnit.SECONDS,
new LinkedBlockingQueue<>(128), // ✅ 有界队列!
new ThreadFactory() {
private final AtomicInteger count = new AtomicInteger(1);
@Override
public Thread newThread(Runnable r) {
return new Thread(r, "App-Pool-" + count.getAndIncrement());
}
},
new ThreadPoolExecutor.CallerRunsPolicy() // ✅ 自我保护
);
public static void execute(Runnable task) {
executor.execute(task);
}
public static Future<?> submit(Runnable task) {
return executor.submit(task);
}
}5.2 AsyncTask 的线程池(已废弃)
// API 11+ 后 AsyncTask 内部线程池:
private static final int CORE_POOL_SIZE = 5;
private static final int MAXIMUM_POOL_SIZE = 128;
private static final int KEEP_ALIVE = 1;
private static final BlockingQueue<Runnable> sPoolWorkQueue =
new LinkedBlockingQueue<Runnable>(10); // 有界队列 10
AsyncTask在 Android 3.0+ 默认是串行执行(SERIAL_EXECUTOR),可通过executeOnExecutor()改为并行。
5.3 Kotlin 协程中的线程池
// Dispatchers.Default —— 对应 JVM 的 ForkJoinPool.commonPool() // 线程数 = CPU 核心数(至少 2) // Dispatchers.IO —— 共享 Default 的线程,但最大线程数为 64 // 适合阻塞 IO 操作 // 自定义: val customDispatcher = Executors.newFixedThreadPool(4).asCoroutineDispatcher()
六、高频
Q1:submit() 和 execute() 有什么区别?
| execute() | submit() | |
|---|---|---|
| 返回值 | void | Future<T> |
| 异常处理 | 异常直接抛出,无法捕获 | 异常封装在 Future 中,调用 get() 时抛出 |
| 参数 | 只能传 Runnable | 可传 Runnable 和 Callable |
Future<Integer> future = executor.submit(() -> 42); Integer result = future.get(); // 阻塞获取结果
Q2:如何优雅关闭线程池?
executor.shutdown(); // 优雅关闭:不再接受新任务,等待已有任务完成
// executor.shutdownNow(); // 强制关闭:尝试中断正在执行的任务
try {
if (!executor.awaitTermination(60, TimeUnit.SECONDS)) {
executor.shutdownNow(); // 超时后强制关闭
}
} catch (InterruptedException e) {
executor.shutdownNow();
}Q3:核心线程数如何设置?
| 场景 | 公式/建议 |
|---|---|
| CPU 密集型(计算、加密) | CPU 核心数 + 1 |
| IO 密集型(网络、文件) | CPU 核心数 * 2 或更大 |
| 混合型 | 拆分为两个线程池 |
Android 中获取 CPU 核心数:
Runtime.getRuntime().availableProcessors()
Q4:ThreadPoolExecutor 中哪个参数最危险?为什么?
workQueue 和 maximumPoolSize 的组合最危险。
如果用 无界队列(如
LinkedBlockingQueue不指定容量),maximumPoolSize将永远失效,因为队列永远不会满,线程数永远不会超过corePoolSize。这会导致任务无限堆积,最终 OOM。
Q5:FutureTask 是什么?原理?
FutureTask 实现了 RunnableFuture(继承 Runnable + Future)。
内部维护一个状态机(
NEW→COMPLETING→NORMAL/EXCEPTIONAL)get()方法会阻塞,依赖AQS(AbstractQueuedSynchronizer)实现等待/唤醒任务执行完成后通过
UnsafeCAS 修改状态,并唤醒等待线程
Q6:线程池中的线程异常了会怎样?
execute()提交:异常抛出,线程终止,线程池会创建新线程替代submit()提交:异常被捕获封装到Future,线程不会终止建议:在
Runnable.run()内部加try-catch,或使用Thread.setUncaughtExceptionHandler()
Q7:如何监控线程池运行状态?
// 常用指标 executor.getPoolSize(); // 当前线程数 executor.getActiveCount(); // 活跃线程数 executor.getQueue().size(); // 队列积压数 executor.getCompletedTaskCount(); // 已完成任务数 executor.getTaskCount(); // 总任务数
可结合这些指标做动态告警(如队列积压超过阈值时扩容或报警)。
Q8:Android 主线程和子线程通信,线程池如何配合 Handler?
// 子线程池执行任务,结果通过 Handler 抛回主线程
Handler mainHandler = new Handler(Looper.getMainLooper());
executor.execute(() -> {
final Bitmap bitmap = loadBitmap(url); // 耗时操作
mainHandler.post(() -> imageView.setImageBitmap(bitmap)); // UI 更新
});更现代的做法:使用 Kotlin 协程
withContext(Dispatchers.IO) { ... }自动切换。
Q9:CachedThreadPool 为什么适合短异步任务?
corePoolSize = 0,所有线程都可回收SynchronousQueue不存储任务,来任务立即创建线程执行keepAliveTime = 60s,线程空闲 60 秒后回收适合大量短生命周期的任务,避免频繁创建/销毁线程的开销
Q10:线程池 + CountDownLatch 的经典使用场景?
// 并发请求多个接口,等待全部完成后统一处理
CountDownLatch latch = new CountDownLatch(3);
for (String url : urls) {
executor.execute(() -> {
try {
download(url);
} finally {
latch.countDown();
}
});
}
latch.await(); // 阻塞等待所有任务完成
mergeResults();七、思维导图总结
线程池
├── 为什么用? → 复用、控制、管理
├── 核心类 ThreadPoolExecutor
│ ├── 7 大参数
│ ├── 任务提交流程(core → queue → max → reject)
│ └── 4 种拒绝策略
├── 队列类型(SynchronousQueue / LinkedBlockingQueue / ArrayBlockingQueue)
├── Executors 工厂(❌ 不推荐,隐藏 OOM 风险)
├── Android 实践
│ ├── 自定义有界线程池
│ ├── AsyncTask(已废弃,串行/并行)
│ └── Kotlin 协程 Dispatcher
└── 高频考点
├── submit vs execute
├── 优雅关闭
├── 线程数配置
├── FutureTask 原理
└── 线程异常处理到此这篇关于Java Android线程池实践指南及高频问题的文章就介绍到这了,更多相关Java线程池详解内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!


最新评论