Android 的线程和线程池

在操作系统中,线程是操作系统调度的最小单元,同时线程又是一种受限的系统资源,即线程不可能无限制地产生,并且线程的创建和销毁都会有相应的开销。在 Android 中除了 Thread 以外,还有 AsyncTask、IntentService 以及 HandlerThread 都扮演着线程的角色。

主线程和子线程

在 Java 中默认情况下一个进程只有一个线程,这个就是主线程,除主线程以外的线程都叫子线程,Android 沿用了同样的线程模型。

Android 中的线程形态

AsyncTask
  1. AsyncTask 是一个轻量级的异步任务类,在线程池中执行后台任务,然后把进度和最终结果传递到主线程更新 UI。
  2. AsyncTask 是一个抽象的泛型类,提供了 ParamsProgressResult 三个泛型参数。如果不需要传递具体的参数,参数类型可以使用 Void 来代替。

    1
    public abstract class AsyncTask <Params, Progress, Result>
  3. AsyncTask 提供 4 个核心方法:

    1. onPreExecute():在主线程中执行,一般用于做一些准备工作。
    2. doInBackground(Params… params):在线程池中执行异步任务,params 为输入参数。在此方法中可以调用 publishProgress(Progress) 方法来更新任务进度。最终需要返回结果给 onPostExecute 方法。
    3. onProgressUpdate(Progess… values):在主线程中执行,任务进度更新时会调用此方法。
    4. onPostExecute(Result result):在主线程中执行,result 为异步任务中返回的结果。
  4. AsyncTask 使用时的注意点:
    1. AsyncTask 的类必须在主线程中加载,这一过程在 Android 4.1 及以上版本被系统自动完成(ActivityThread 的 main 方法会调用 AsyncTask 的 init 方法)。
    2. AsyncTask 的对象必须在主线程中创建。
    3. execute 方法必须在 UI 线程中调用。
    4. 不要在程序中直接调用 onPreExecuteonPostExecutedoInBackgroundonProgressUpdate 方法。
    5. 一个 AsyncTask 对象只能执行一次,即只能调用一次 execute 方法,否则会报错。
    6. 在 Android 1.6 之前,AsyncTask 是串行执行任务的,Android 1.6 的时候开始采用线程池处理并行任务,但是从 Android 3.0 开始,又换回了串行执行任务的方式,不过我们可以通过 executeOnExecutor 方法执行并行任务。
AsyncTask 的工作原理

AsyncTask 中有两个线程池(SerialExecutor 和 THREAD_POOL_EXECUTOR)和一个 Handler(InternalHandler)。其中线程池 SerialExecutor 用于任务的排队,而线程池 THREAD_POOL_EXECUTOR 用于真正地执行任务,InternalHandler 用于将执行环境从线程池切换到主线程。

HandlerThread
  1. HandlerThread 继承了 Thread,它是一种可以使用 Handler 的 Thread。
  2. HandlerThread 的实现就是在其 run 方法中通过 Looper.prepare() 来创建消息队列,通过 Looper.loop() 来开启消息循环。
  3. HandlerThread 的 run 方法是一个无限循环,当不需要使用时,要通过 quit 或者 quitSafely 方法来终止线程。
  4. HandlerThread 的一个典型使用场景就是 IntentService。
IntentService
  1. IntentService 是一种特殊的抽象 Service,内部封装了 HandlerThread 和 Handler,比较适合执行一些高优先级的后台任务,不容易被杀死。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    @Override
    public void onCreate() {
    super.onCreate();
    HandlerThread thread = new HandlerThread("IntentService[" + mName + "]");
    thread.start();

    mServiceLooper = thread.getLooper();
    mServiceHandler = new ServiceHandler(mServiceLooper);
    }
  2. IntentService 调用 stopSelf(int startId) 方法来尝试停止服务,会等待所有消息都处理完毕后才真正终止服务。

  3. IntentService 的执行顺序是按外界发起的顺序去执行的。

image

Android 中的线程池

  1. 线程池的优点:
    • 重用线程池中的线程,避免因为线程的创建和销毁所带来的性能开销。
    • 能有效控制线程池的最大并发数,避免大量线程之间因互相抢占系统资源而导致的阻塞现象。
    • 能够对线程进行简单的管理,并提供定时执行以及指定间隔循环执行等功能。
  2. Android 中线程池的概念源于 Java 中的 Executor,真正实现为 ThreadPoolExecutor。
ThreadPoolExecutor

ThreadPoolExecutor 常用构造方法如下:

1
2
3
4
5
6
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory)

  • corePoolSize:线程池的核心线程数,默认情况下核心线程会一直存活。如果将 ThreadPoolExecutor 的 allowCoreThreadTimeOut 属性设置为 true,闲置时间超过 keepAliveTime 会被终止。
  • maximumPoolSize:线程池所能容纳的最大线程数(核心线程 + 非核心线程),当活动线程数达到这个数值后,后续任务会被阻塞。
  • keepAliveTime:非核心线程闲置时的超时时长,超过这个时长,非核心线程会被回收。当 ThreadPoolExecutor 的 allowCoreThreadTimeOut 属性设置为 true 时,同样作用于核心线程。
  • unit:指定 keepAliveTime 参数的时间单位。
  • workQueue:线程池中的任务队列,通过线程池的 execute 方法提交的 Runnable 对象会被存储在这个参数中。
  • threadFactory:线程工厂,为线程池提供创建新线程的功能。

ThreadPoolExecutor 执行任务时大致遵循如下规则:

  1. 如果线程池中的线程数量未达到核心线程的数量,那么会直接启动一个核心线程来执行任务。
  2. 如果线程池中的线程数量达到或超过核心线程的数量,那么任务会被插入任务队列等待执行。
  3. 如果步骤2中无法将任务插入任务队列中,这往往是由于任务队列已满,这时如果线程数量未到达线程池规定的最大值,那么会立即启动一个非核心线程来执行任务。
  4. 如果步骤3中的线程数量已经达到了线程池规定的最大值,那么就会拒绝执行此任务。
线程池的分类

Android 中最常见的四类线程池都是通过 Executors 的静态方法创建的。

  1. FixedThreadPool - 线程数量固定的线程池,只有核心线程

    1
    2
    3
    4
    5
    public static ExecutorService newFixedThreadPool(int nThreads) {
    return new ThreadPoolExecutor(nThreads, nThreads,
    0L, TimeUnit.MILLISECONDS,
    new LinkedBlockingQueue<Runnable>());
    }
  2. CachedThreadPool - 线程数量不固定的线程池,只有非核心线程

    1
    2
    3
    4
    5
    public static ExecutorService newCachedThreadPool() {
    return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
    60L, TimeUnit.SECONDS,
    new SynchronousQueue<Runnable>());
    }
  3. ScheduledThreadPool - 核心线程数量固定,非核心线程数量没有限制的线程池,主要用于执行定时任务和具有固定周期的任务

    1
    2
    3
    4
    5
    6
    7
    8
    9
    public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
    return new ScheduledThreadPoolExecutor(corePoolSize);
    }

    public ScheduledThreadPoolExecutor(int corePoolSize) {
    super(corePoolSize, Integer.MAX_VALUE,
    DEFAULT_KEEPALIVE_MILLIS, MILLISECONDS,
    new DelayedWorkQueue());
    }
  4. SingleThreadExecutor - 只有一个核心线程的线程池,确保了所有的任务都在同一个线程中按顺序执行

    1
    2
    3
    4
    5
    6
    public static ExecutorService newSingleThreadExecutor() {
    return new FinalizableDelegatedExecutorService
    (new ThreadPoolExecutor(1, 1,
    0L, TimeUnit.MILLISECONDS,
    new LinkedBlockingQueue<Runnable>()));
    }