一句话说透Android里面AsyncTask线程池细节

103 阅读3分钟

一句话总结:

用餐厅厨房类比理解:
AsyncTask的线程池就像餐厅的后厨系统

  • 厨师(线程) :处理订单(任务)
  • 传菜口(Handler) :把做好的菜(结果)送回前厅(主线程)
  • 排班表(线程池配置) :决定有多少厨师同时工作

一、AsyncTask线程池核心配置

1. 双线程池结构

// 源码关键参数(Android 11之前)  
private static final int CORE_POOL_SIZE = 1; // 核心线程数  
private static final int MAXIMUM_POOL_SIZE = 20; // 最大线程数  
private static final int KEEP_ALIVE_SECONDS = 3; // 空闲线程存活时间  
 
public static final Executor THREAD_POOL_EXECUTOR = new ThreadPoolExecutor(  
    CORE_POOL_SIZE,   
    MAXIMUM_POOL_SIZE,  
    KEEP_ALIVE_SECONDS,   
    TimeUnit.SECONDS,  
    new LinkedBlockingQueue<Runnable>(128), // 任务队列容量  
    sThreadFactory  
);  

参数解读

  • 核心厨师:至少保持1个线程存活
  • 最大厨师数:最多可扩容到20个线程
  • 临时工存活:空闲3秒后回收多余线程
  • 订单队列:最多堆积128个待处理任务

2. 版本行为差异

Android版本默认执行模式特性说明
≤ 1.5(Cupcake)并行执行多个任务同时处理
1.6(Donut)~ 2.3(Gingerbread)串行执行任务排队逐个处理
≥ 3.0(Honeycomb)可控模式可通过executeOnExecutor()自定义

二、任务调度流程

1. 默认串行模式流程

// 实际执行顺序控制  
val SERIAL_EXECUTOR = object : Executor {  
    private val mTasks = ArrayDeque<Runnable>()  
    private var mActive: Runnable? = null  
 
    override fun execute(r: Runnable) {  
        mTasks.offer(Runnable  {  
            try {  
                r.run()   
            } finally {  
                scheduleNext()  
            }  
        })  
        if (mActive == null) scheduleNext()  
    }  
}  

工作流程

  1. 新任务加入队列尾部
  2. 当前任务完成后触发下一个
  3. 保证任务顺序执行

2. 并行模式启动方式

// 使用原生线程池实现并行  
task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR)   
 
// 自定义线程池示例  
val customExecutor = ThreadPoolExecutor(  
    4, // 核心线程  
    10, // 最大线程  
    5, TimeUnit.SECONDS,  
    LinkedBlockingQueue(100)  
)  
task.executeOnExecutor(customExecutor)   

三、线程池工作示意图

新任务到达 → 核心线程是否空闲?  
   ├─ 是 → 立即执行  
   └─ 否 → 进入等待队列?  
       ├─ 队列未满 → 加入队列  
       └─ 队列已满 → 创建新线程(不超过MAXIMUM_POOL_SIZE)  
           ├─ 成功 → 执行任务  
           └─ 失败 → 触发拒绝策略  

四、实际开发中的坑与解决方案

坑1:任务堆积导致ANR

错误现象

  • 大量短任务快速提交导致队列阻塞

优化方案

// 监控队列负载  
val executor = AsyncTask.THREAD_POOL_EXECUTOR as ThreadPoolExecutor  
if (executor.queue.size  > 50) {  
    // 触发降级策略  
}  

坑2:内存泄漏

危险代码

class MyActivity : Activity() {  
    fun doTask() {  
        // 匿名内部类隐式持有Activity引用  
        object : AsyncTask<Void, Void, Void>() {  
            override fun doInBackground(vararg params: Void): Void {  
                // 长时间运行任务  
                return null  
            }  
        }.execute()  
    }  
}  

正确方案

// 使用弱引用 + 静态内部类  
class SafeTask(activity: MyActivity) : AsyncTask<Void, Void, Void>() {  
    private val weakRef = WeakReference(activity)  
 
    override fun doInBackground(vararg params: Void): Void {  
        weakRef.get()?.apply  {  
            // 操作前检查是否存活  
        }  
        return null  
    }  
}  

五、替代方案推荐

场景推荐方案优势
简单后台任务Kotlin协程轻量级、结构化并发
长时间运行WorkManager生命周期感知、省电优化
即时UI更新Handler + Executor精细控制线程切换
复杂数据流RxJava强大的操作符支持

AsyncTask线程池使用口诀:
核心线程打头阵,队列满了才扩容
串行就像单车道,并行车道可多开
存活时间要合理,避免资源太浪费
内存泄漏是大敌,弱引用保平安!