Handler Executor 提交边界
Handler 和 Java Executor 都接受 Runnable,但它们拥有的状态不同:Handler 把任务变成 Message,交给一个既有 Looper 的单线程队列;Executor 只定义提交接口,线程、队列、拒绝和 并发由实现决定;ExecutorService 再增加 Future、关闭和等待终止;FutureTask 保存结果、 异常和取消状态。
本文面向已经读过 HandlerExecutor 提交语义、 AsyncTask 任务生命周期、消息队列性能代价 和 HandlerThread 生命周期 的读者。Android 部分依据 android-17.0.0_r1 的 AOSP 源码;Java 并发部分依据 OpenJDK 21 src.zip 中的 标准库源码。本文不使用归档文章的性能数字或工厂默认值,重点回答:提交成功到底意味着 什么,结果和异常由谁拥有,取消能否停止工作,关闭如何处理等待任务,以及如何在 Handler 和 Executor 之间做准确适配。
读完后,读者应能从一次 API 调用定位队列/线程 owner,解释为什么 HandlerExecutor.execute() 没有 Future,区分 FutureTask.cancel() 与 Handler 消息移除,并根据需要选择 Handler、单线程 Executor、线程池或两者组合。
1. 接口层级
源码文件:java.base/java/util/concurrent/Executor.java
OpenJDK 的 Executor 只有一个 execute(Runnable) 方法,接口文档明确说明实现可以在调用线程、 新线程或池线程执行,接口本身不承诺异步。
public interface Executor {
void execute(Runnable command);
}源码文件:java.base/java/util/concurrent/ExecutorService.java
ExecutorService 在 Executor 之上增加 submit、Future、shutdown、shutdownNow 和 awaitTermination。这些是标准库源码的能力,不是 Android HandlerExecutor 自动拥有的能力。
public interface ExecutorService extends Executor, AutoCloseable {
<T> Future<T> submit(Callable<T> task);
void shutdown();
List<Runnable> shutdownNow();
boolean isTerminated();
boolean awaitTermination(long timeout, TimeUnit unit)
throws InterruptedException;
}| 类型 | 任务 owner | 结果 | 关闭能力 |
|---|---|---|---|
Handler | MessageQueue/Looper | 无内置 Future | Looper quit |
HandlerExecutor | 被包装 Handler | 无 Future | 委托 Handler 队列 |
Executor | 实现自定 | 无内置结果 | 未定义 |
ExecutorService | ExecutorService | Future | shutdown/await |
2. Handler 适配
源码文件:frameworks/base/core/java/android/os/HandlerExecutor.java
HandlerExecutor 的实现只有一个 Handler 字段。execute() 调用 Handler.post();返回 false 时转换为 RejectedExecutionException。
public class HandlerExecutor implements Executor {
private final Handler mHandler;
public HandlerExecutor(@NonNull Handler handler) {
mHandler = Preconditions.checkNotNull(handler);
}
@Override
public void execute(Runnable command) {
if (!mHandler.post(command)) {
throw new RejectedExecutionException(mHandler + " is shutting down");
}
}
}成功只表示 Message 入队,不表示 Runnable 已开始或完成;HandlerExecutor 不创建线程、不返回 Future、不提供取消句柄、不管理 HandlerThread 生命周期。
3. Handler 提交
源码文件:frameworks/base/core/java/android/os/Handler.java
Handler 的 post() 先生成带 callback 的 Message,再走 sendMessageDelayed/AtTime;目标线程 来自构造时保存的 Looper。
public final boolean post(@NonNull Runnable r) {
return sendMessageDelayed(getPostMessage(r), 0);
}
private static Message getPostMessage(Runnable r) {
Message m = Message.obtain();
m.callback = r;
return m;
}源码文件:frameworks/base/core/java/android/os/LegacyMessageQueue/MessageQueue.java
入队会按 when 排序;队列退出时返回 false 并回收 Message。
if (mQuitting) {
msg.recycle();
return false;
}
msg.markInUse();
msg.when = when;
// 按 when 插入链表4. 线程池提交
源码文件:java.base/java/util/concurrent/ThreadPoolExecutor.java
OpenJDK ThreadPoolExecutor.execute() 的行为由 worker 数量、work queue 和拒绝处理器共同 决定;它不是单一的“提交到某条线程”。源码中的核心路径会尝试创建 worker、入队,再检查 线程池状态,必要时调用 RejectedExecutionHandler。
public void execute(Runnable command) {
if (command == null) throw new NullPointerException();
int c = ctl.get();
if (workerCountOf(c) < corePoolSize) {
if (addWorker(command, true)) return;
c = ctl.get();
}
if (isRunning(c) && workQueue.offer(command)) {
int recheck = ctl.get();
if (!isRunning(recheck) && remove(command))
reject(command);
else if (workerCountOf(recheck) == 0)
addWorker(null, false);
} else if (!addWorker(command, false)) {
reject(command);
}
}对比 Handler:Handler 的队列 owner 固定为一个 Looper;ThreadPoolExecutor 的并发度、队列容量、 worker 创建时机和拒绝策略都由实例配置。
5. 提交结果
源码文件:frameworks/base/core/java/android/os/HandlerExecutor.java
HandlerExecutor 只能用异常表达队列拒绝,调用者没有任务完成对象。
if (!mHandler.post(command)) {
throw new RejectedExecutionException(mHandler + " is shutting down");
}源码文件:java.base/java/util/concurrent/ExecutorService.java
ExecutorService 的 submit(Callable) 返回 Future,Future 的 get() 会等待并报告结果、异常或 取消。
<T> Future<T> submit(Callable<T> task);
// Future contract: get returns result, throws ExecutionException/CancellationException,
// and may block until completion.两者的提交语义不能互换:
| 结果问题 | HandlerExecutor | ExecutorService.submit |
|---|---|---|
| 当场拒绝 | 抛 RejectedExecutionException | 抛 RejectedExecutionException |
| 已接受 | 无返回对象 | 返回 Future |
| 完成确认 | 需业务 callback/latch | Future.isDone/get |
| 异常读取 | Looper 线程异常路径 | Future.get 抛 ExecutionException |
6. Future 状态
源码文件:java.base/java/util/concurrent/FutureTask.java
FutureTask 用状态值区分 NEW、NORMAL、EXCEPTIONAL、CANCELLED、INTERRUPTING 和 INTERRUPTED; get() 根据最终状态返回 outcome 或抛异常。
private volatile int state;
private static final int NEW = 0;
private static final int NORMAL = 2;
private static final int EXCEPTIONAL = 3;
private static final int CANCELLED = 4;
public V get() throws InterruptedException, ExecutionException {
int s = state;
if (s <= COMPLETING) s = awaitDone(false, 0L);
return report(s);
}Handler Message 没有等价的完成状态字段;Message 的 flags/in-use 表示队列和对象生命周期, 不是业务结果状态。
7. 取消语义
源码文件:java.base/java/util/concurrent/FutureTask.java
FutureTask.cancel(mayInterruptIfRunning) 先尝试 CAS 状态;参数为 true 时尝试中断 runner, 但 OpenJDK 文档明确这是 best effort,任务是否响应由任务代码决定。
public boolean cancel(boolean mayInterruptIfRunning) {
if (!(state == NEW && STATE.compareAndSet(this, NEW,
mayInterruptIfRunning ? INTERRUPTING : CANCELLED))) {
return false;
}
if (mayInterruptIfRunning) {
Thread t = runner;
if (t != null) t.interrupt();
STATE.setRelease(this, INTERRUPTED);
}
finishCompletion();
return true;
}源码文件:frameworks/base/core/java/android/os/Handler.java
Handler 取消通过匹配并移除仍在队列中的 Message;已经出队的 Runnable 不会被中断。
public final void removeCallbacks(@NonNull Runnable r) {
mQueue.removeMessages(this, r, null);
}因此“Future.cancel 更强”也不能绝对化:interrupt 不会强制终止不响应中断的阻塞任务;两者 都需要任务本身具备协作式停止协议,只是控制面不同。
8. 关闭语义
源码文件:java.base/java/util/concurrent/ExecutorService.java
shutdown() 停止接收新任务但允许已提交任务完成;shutdownNow() 尝试停止运行任务并返回 尚未开始的 Runnable,且不等待运行任务终止。
void shutdown();
List<Runnable> shutdownNow();
boolean awaitTermination(long timeout, TimeUnit unit)
throws InterruptedException;源码文件:frameworks/base/core/java/android/os/Looper.java
Looper quit() 会立即终止,不保证待处理消息执行;quitSafely() 只处理已经到期消息,未来 延迟消息不会执行。
public void quit() {
mQueue.quit(false);
}
public void quitSafely() {
mQueue.quit(true);
}| 关闭问题 | HandlerThread/Looper | ExecutorService |
|---|---|---|
| 拒绝新任务 | Handler send 返回 false | RejectedExecutionException |
| 等待已提交任务 | 无统一 Future/await API | awaitTermination |
| 返回未开始任务 | 无 | shutdownNow 返回 List |
| 延迟任务 | quitSafely 不执行未来消息 | 取决于队列/实现和 shutdown 语义 |
9. 串行模型
源码文件:frameworks/base/core/java/android/os/HandlerThread.java
一个 HandlerThread 只有一个 Looper,多个 Handler 可以共享同一队列;同一时刻只有一个消息 在该 Looper 线程执行。
HandlerThread thread = new HandlerThread("serial-worker");
thread.start();
Handler handler = new Handler(thread.getLooper());
handler.post(taskA);
handler.post(taskB);源码文件:java.base/java/util/concurrent/Executor.java
Executor 接口本身甚至允许 inline executor;串行或并行不是接口结论,而是实现配置。
Executor inline = Runnable::run;如果需要 Executor 的串行队列,应选择单线程实现或显式 SerialExecutor;不能因为类型名是 Executor 就假设并发,也不能因为类型名是 Handler 就假设任务一定异步到另一个线程。
10. 线程池模型
源码文件:java.base/java/util/concurrent/ThreadPoolExecutor.java
ThreadPoolExecutor 的核心参数包括 corePoolSize、maximumPoolSize、workQueue、keepAliveTime 和 RejectedExecutionHandler。队列是否有界、worker 是否扩容、任务被拒绝还是排队,都由这些 参数共同决定。
new ThreadPoolExecutor(
2, 4, 30, TimeUnit.SECONDS,
new ArrayBlockingQueue<>(100),
threadFactory,
rejectedExecutionHandler);Handler 的 MessageQueue 没有 FutureTask 那种 worker 扩缩容/拒绝策略配置;它的优势是 Android Looper 集成,Executor 的优势是并发和关闭控制。两者不应以单一“性能更好”排序。
11. 适配组合
源码文件:frameworks/base/core/java/android/os/HandlerThread.java
Android 17 的 HandlerThread 直接提供 getThreadExecutor(),它返回由该线程 Handler 包装的 HandlerExecutor;这条适配保留单线程 Looper 语义,同时满足只接收 Executor 的 API。
public Executor getThreadExecutor() {
if (mExecutor == null) {
mExecutor = new HandlerExecutor(getThreadHandler());
}
return mExecutor;
}组合后的 Executor 仍没有 Future/await;若 API 需要结果,应在任务协议中额外使用 Future、 callback 或 CompletableFuture,不能从 getThreadExecutor() 推导结果能力。
12. 失败路径
源码文件:frameworks/base/core/java/android/os/HandlerExecutor.java
HandlerExecutor 的失败只有 Handler.post 返回 false 这一层;任务运行时异常仍沿 Looper dispatch 路径传播。
public void execute(Runnable command) {
if (!mHandler.post(command)) {
throw new RejectedExecutionException(mHandler + " is shutting down");
}
}源码文件:java.base/java/util/concurrent/ThreadPoolExecutor.java
ThreadPoolExecutor 的失败可能来自 null command、池关闭、队列满和拒绝处理器;FutureTask 则 把 Callable 异常保存到 outcome,直到 get() 报告。
if (command == null) throw new NullPointerException();
if (!addWorker(command, false)) {
reject(command);
}这两种失败都不是“任务执行失败”的同义词:拒绝发生在提交阶段,Callable 异常发生在执行阶段。
13. 可追踪主线
图中两条路径的关键差异是结果 owner:Handler 路径在 Looper dispatch 后没有 Future;ExecutorService 路径由 FutureTask 保存状态并支持 get/cancel。
14. 验证输入
源码文件:frameworks/base/core/java/android/os/HandlerExecutor.java
第一组验证提交:HandlerThread 运行时调用 HandlerExecutor.execute,断言 Runnable 在线程 TID 上执行;quit 后再次 execute,断言 RejectedExecutionException。
源码文件:java.base/java/util/concurrent/FutureTask.java
第二组验证结果:用 ExecutorService.submit Callable,分别正常返回、抛异常和 cancel,断言 Future.get 的结果/ExecutionException/CancellationException;对比 Handler 任务没有对应 Future 状态。
第三组验证关闭:HandlerThread quitSafely 与 ExecutorService.shutdown/shutdownNow 分别安排 未来任务、运行中任务和新提交,记录已开始、未开始、拒绝和终止状态。不要用单个实现的结果 概括所有 Executor。
15. 复查命令
源码文件:
frameworks/base/core/java/android/os/HandlerExecutor.javaframeworks/base/core/java/android/os/HandlerThread.javaframeworks/base/core/java/android/os/Handler.javaframeworks/base/core/java/android/os/Looper.javajava.base/java/util/concurrent/Executor.javajava.base/java/util/concurrent/ExecutorService.javajava.base/java/util/concurrent/FutureTask.javajava.base/java/util/concurrent/ThreadPoolExecutor.java
rg -n "class HandlerExecutor|RejectedExecutionException|mHandler.post" \
frameworks/base/core/java/android/os/HandlerExecutor.java
rg -n "getThreadExecutor|getThreadHandler|quitSafely|Looper.loop" \
frameworks/base/core/java/android/os/HandlerThread.java
rg -n "removeCallbacks|sendMessageDelayed|mQueue" \
frameworks/base/core/java/android/os/Handler.java16. 选择边界
选择 Handler 时,核心问题是是否需要 Android Looper、Message、同步屏障、主线程和 framework 回调集成;选择 ExecutorService 时,核心问题是并发度、队列容量、Future 结果、拒绝策略和 关闭等待。HandlerExecutor 只解决接口适配,不把 Handler 变成线程池,也不把 Executor 变成 Android 生命周期组件。
比较时先写清提交结果、任务完成、异常、取消和关闭五个契约,再用 Android AOSP 与具体 JDK 版本源码验证。这样比用“Handler 轻量、Executor 并行”的一句话结论更能指导真实源码阅读和 故障定位。
