11 Android的线程和线程池

2019/03/06 posted in  Android开发艺术探索
  • AsyncTask封装了线程池和Handler,它主要是为了方便开发者在子线程中更新UI。
  • HandlerThread是一种具有消息循环的线程,在它的内部可以使用Handler。
  • IntentService是一个服务,系统对其进行了封装使其可以更方便地执行后台任务,IntentService 内部采用HandlerThread来执行任务,当任务执行完毕后IntentService 会自动退出。 它不容易被系统杀死从而可以尽量保证任务的执行。

11.1 主线程和子线程

  • 主线程:进程所拥有的线程,主要处理界面交互相关的逻辑。
  • 子线程:除主线程之外都是子线程,主要用于执行耗时操作。

11.2 Android中的线程形态

11.2.1 AsyncTask

轻量级的异步任务类,可以执行后台任务以及在子线程中进行UI操作。

主要方法:

  1. onPreExecute()
  2. doInBackground(Params…params):
  3. onProgressUpdate(Progress…values)
  4. onPostExecute(Result result)
  5. onCancelled()
  6. execute(Params...params)

11.2.2 AsyncTask的工作原理

  1. AsyncTask构造方法中:
    1. 实例化Handler,类型是InternalHandler,用于切换到主线程
    2. 实例化mWorker,类型是WorkerRunnable,封装了Params和Result;
    3. 实例化mFuture,类型是FutureTask,参数为mWorker。FutureTask是一个并发类,在这里它充当了Runnable的作用。
  2. 执行任务
    1. execute方法会调用executeOnExecutor(sDefaultExecutor, params)
      1. 参数sDefaultExecutor,类型为SerialExecutor,是一个串行的线程池,一个进程中所有的AsyncTask全部在这个串行的线程池中排队执行。
      2. SerialExecutor#execute。(1) ArrayDeque.offer(Runnable)。把FutureTask对象插入到任务队列mTasks中;(2) scheduleNext()。如果没有正在活动的任务,或者当一个任务执行完后,会调用scheduleNext方法执行下一个任务,直到所有的任务都被执行。scheduleNext中,调用THREAD_POOL_EXECUTOR.execute()真正地执行任务
    2. executeOnExecutor
      1. 调用onPreExecute()
      2. exec.execute(mFuture);线程池执行。
  3. 响应结果
    1. mWorker的call()中调用doInBackground(mParams)postResult()
    2. postResult中,Handler调用sendToTarget()发送消息。
    3. InternalHandler中,onProgressUpdatefinish
  4. 结束或取消onCancelledonPostExecute

    从Android 3.0 开始,默认情况下AsyncTask是串行执行的,可以调用executeOnExecutor方法并行执行。

11.2.3 HandlerThread

  • HandlerThread本质上是一个线程类,它继承了Thread;
  • HandlerThread有自己的内部Looper对象,可以进行looper循环;
  • 通过获取HandlerThread的looper对象传递给Handler对象,可以在handleMessage方法中执行异步任务。
  • 创建HandlerThread后必须先调用HandlerThread.start()方法,Thread会先调用run方法,创建Looper对象。
  • 当不需要HandlerThread时,通过HandlerThread.quit()/quitSafely()方法来终止线程的执行

11.2.4 IntentService

可自动创建子线程来执行任务,且任务执行完毕后自动退出。

  • 在IntentService.onCreate()里创建一个Handle对象即HandlerThread,利用其内部的Looper会实例化一个ServiceHandler对象;
  • 任务请求的Intent会被封装到Message并通过ServiceHandler发送给Looper的MessageQueue,最终在HandlerThread中执行;
  • 在ServiceHandler.handleMessage()中会调用IntentService.onHandleIntent(),可在该方法中处理后台任务的逻辑。

11.3 Android中的线程池

线程池的优点:

  • 重用线程池中的线程,避免线程的创建和销毁带来的性能消耗;
  • 有效控制线程池的最大并发数,避免大量的线程之间因互相抢占系统资源而导致阻塞现象;
  • 能够进行线程管理,提供定时/循环间隔执行等功能。

Android中的线程池的概念来源于Java中的Executor, Executor是一个接口,真正的线程池的实现为ThreadPoolExecutor。ThreadPoolExecutor 提供了一系列参数来配置线程池,通过不同的参数可以创建不同的线程池。

11.3.1 ThreadPoolExecutor

ThreadPoolExecutor构造方法:

public ThreadPoolExecutor(
    int corePoolSize,   //核心线程数
    int maximumPoolSize,//最大线程数
    long keepAliveTime,//非核心线程超时时间
    TimeUnit unit,//keepAliveTime参数的时间单位
    BlockingQueue<Runnable> workQueue,//任务队列
    ThreadFactory threadFactory,//线程工厂,可创建新线程
    RejectedExecutionHandler handler//饱和策略
)

ThreadPoolExecutor的默认工作策略:

  • 如果线程池中的线程数量未达到核心线程数,则会直接启动一个核心线程执行任务。
  • 若线程池中的线程数量已达到或者超过核心线程数量,则任务会被插入到任务列表等待执行。
  • 若任务无法插入到任务列表中,往往由于任务列表已满,此时如果:
    • 线程数量未达到线程池最大线程数,则会启动一个非核心线程执行任务;
    • 线程数量已达到线程池规定的最大值,则拒绝执行此任务

如果条件为否:核心线程 > 任务列表 > 非核心线程 > 拒接任务。

AsyncTask的THREAD_POOL_EXECUTOR线程池配置参数:

  • 核心线程数:CPU_COUNT - 1,最小为2最大为4;
  • 线程池的最大线程数:CPU_COUNT * 2+ 1;
  • 核心线程无超时机制,非核心线程在闲置时的超时时间为30秒;
  • 任务队列的容量为128。

11.3.2 线程池的分类

  • FixedThreadPool:
    • 含义:线程数量固定的线程池,所有线程都是核心线程,当线程空闲时不会被回收。
    • 特点:能快速响应外界请求。
  • CachedThreadPool:
    • 含义:线程数量不定的线程池(最大线程数为Integer.MAX_VALUE),只有非核心线程,空闲线程有超时机制,60s超时回收。
    • 特点:适合于执行大量的耗时较少的任务
  • ScheduledThreadPool:
    • 含义:核心线程数量固定,非核心线程数量不定。
    • 特点:定时任务和固定周期的任务。
  • SingleThreadExecutor:
    • 含义:只有一个核心线程,可确保所有的任务都在同一个线程中按顺序执行。
    • 特点:无需处理线程同步问题。