huazi

huazi

《Android开发艺术探索》笔记-第11章-线程和线程池

AsyncTask 封装了线程池和 handler,主要 hi 为了方便开发者在子线程中更新 UI。
HandlerThread 是一种具有消息循环的线程,他的内部可以使用 Handler。
IntentService 是一个服务,系统对其进行了封装,以便执行后台任务,IntentService 内部采用 HanderThread 来执行任务,当任务完成后会自动退出 IntentService,他是一种服务,不容易被系统杀死从而尽量保证任务的执行。

在操作系统中,线程是操作系统调度的最小单元,同时线程也是一种受限的系统资源,即线程不能无限的产生,线程的创建都有相应的开销,线程不能做到绝对的并行,除非线程数量小于等于 CPU 核心数。正确的做法是采用线程池,一个线程池会缓存一定数量的线程,通过线程池可以避免因为频繁的创建线程带来的系统开销。Android 的线程来源于 Java,主要是通过 Executor 来派生特定类型的线程池。

AysncTask#

AysncTask 是一种轻量级的异步任务,他可以在线程池中执行后台任务,然后把执行进度和最终结果传递给主线程。
AysncTask 封装了 Thread 和 Handler。AysncTask 并不适合进行特别耗时的任务,对于特别耗时的任务来说建议使用线程池。

AysncTask 使用过程中的一些限制:

  1. 一个 AysncTask 对象只能调用一次 execute 方法,否则会报运行异常。
  2. AysncTask 默认采用一个串行执行任务,我们可以通过 executeOnExecutor 方法来并行执行任务。

通过查看源码后发现:
26 版本之后已经可以在子线程中创建 AysncTask,因为默认构造函数使用的是通过 MainLooper 创建的 handler。

 public AsyncTask() {
     this((Looper) null);
 }
 
 public AsyncTask(@Nullable Looper callbackLooper) {
        mHandler = callbackLooper == null || callbackLooper == Looper.getMainLooper()
            ? getMainHandler()
            : new Handler(callbackLooper);
// 省略其他代码
}


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

看一下 SerialExecutor 的实现:

private static class SerialExecutor implements Executor {
        final ArrayDeque<Runnable> mTasks = new ArrayDeque<Runnable>();
        Runnable mActive;

        public synchronized void execute(final Runnable r) {
            mTasks.offer(new Runnable() {
                public void run() {
                    try {
                        r.run();
                    } finally {
                        scheduleNext();
                    }
                }
            });
            if (mActive == null) {
                scheduleNext();
            }
        }

        protected synchronized void scheduleNext() {
            if ((mActive = mTasks.poll()) != null) {
                THREAD_POOL_EXECUTOR.execute(mActive);
            }
        }
    }

从上面代码可以分析 AsyncTask 的排队过程。首先系统会把 AsyncTask 的 params 参数封装成 FutureTask 对象,FutureTask 是个并发类,在这里他充当了 Runnable 的作用。接着这个 FutureTask 会交给 SerialExecutor 的 execut 方法去处理, SerialExecutor 的 execut 方法首先会把 FutureTask 对象插入到任务队列 mTasks 中,如果这个时候没有正在活动的 AsyncTask 任务,那么就会调用 SerialExecutor 的 scheduleNext 方法来执行下一个任务,同时 AsyncTask 执行完任务后,AsyncTask 会继续执行其他任务直到所有的任务都被执行完毕为止。

HandlerThread#

HandlerThread 继承自 Thread , 它是一种可以使用 handler 的 Thread 就是 在 run 方法中通过 Looper.prepare 来创建消息队列,并通过 Looper.loop () 来开启消息循环。

普通 Thread 主要用于在 run 方法中执行一个耗时任务,而 HandlerThread 内部创建了消息队列,外界需要通过 Handler 的消息方式来通知 HandlerThread 执行具体任务。HandlerThread 的具体使用场景是 IntentService 。
由于 HandlerThread 的 run 是一个无线循环,因此当明确不需要 HandlerThread 时可以通过 quit 或 quitSafely 来终止线程的执行。

IntentService#

IntentService 是一种特殊的 Service,它继承自 Service 并且是个抽象类,必须创建自己的子类才能使用。IntentService 可用于执行后台的耗时任务,当任务执行后自动停止,由于是服务,所以优先级比单纯的线程高,所以 IntentService 适合高优先级的后台任务。IntentService 封装了 HandlerThread 和 Handler.

每执行一个后台任务,就必须启动一次 IntentService, 而 IntentService 内部则通过消息的方式向 HandlerThread 请求执行任务,Handler 中的 Looper 是顺序处理消息的,这就意味着 IntentService 也是顺序后台执行任务的,多个任务同时存在时,后台任务会按照外界发起的顺序排队执行。

onHandleIntent 可以对不同的任务做处理,当 onHandleIntent 执行结束后 IntentService 会通过 stopSelf 尝试停止服务,当 IntentService onDestory 时会停止 looper,防止出现线程无法释放的问题。

Android 中的线程池#

线程池的优点:

  1. 重用线程池中的线程,避免因为线程的创建和销毁带来的性能开销。
  2. 有效控制线程池的最大并发数,避免大量的线程间因互相抢占资源而导致的阻塞的现象。
  3. 能够对线程进行简单的管理,并提供定时执行以及间隔循环执行等功能。

ThreadPoolExecutor 是线程池的真正实现,他的构造方法提供了一系列参数来配置线程池,个参数的意义如下:

  • corePoolSize
    线程池的核心线程数,默认情况下,核心线程会在线程池中一直存活,即使他们处于闲置状态。如果将 ThreadPoolExecutor 的 allowCoreThreadTimeOut 设为 true,那么闲置的核心线程在等待新任务到来时会有超时策略,这个时间间隔由 keepAliveTime 所指定,等待时间超过 keepAliveTime 时间,核心线程就会被终止。

  • maximumPoolSize
    线程池所能容纳的最大线程数,当活动线程数达到这个数值后,后续的新任务会被阻塞。

  • keepAliveTime
    非核心线程闲置的超时时长,超过这个时长非核心线程会被回收,当 ThreadPoolExecutor 的 allowCoreThreadTimeOut 为 true 时,keepAliveTime 同样作用于核心线程。

  • unit
    用于指定 keepAliveTime 参数的时间单位,是个枚举

  • workQueue
    线程池中的任务队列,通过线程池的 execute 方法提交 Runnable 对象会存储在这个参数中。

  • threadFactory
    线程工厂,为线程池提供创建新线程的功能,ThreadFactory 是个接口,它只有一个方法:new Thread (Runnable r)

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

  1. 如果线程池中的线程数量未达到核心线程数的数量,那么会启动一个核心线程来执行任务。
  2. 如果线程池的线程数量已经达到或者超过核心线程的数量,那么任务会被插入到任务队列中排队等待执行。
  3. 如果在步骤 2 中,无法插入到任务队列中,往往是由于任务队列已满,这个时候如果线程数量未达到线程池规定的最大值,那么立即启动一个非核心线程来执行任务。
  4. 如果步骤 3 中的线程数量已经达到线程池规定的最大值,那么就拒绝此任务,ThreadPoolExecutor 会调用 RejectedExecutionHandler 的 rejectExcecution 来通知调用者。
线程池的分类#
  1. FixedThreadPool
    通过 Executors 的 newFixedThreadPool 方法来创建,它是一种线程数量固定的线程池,只有核心线程,并且核心线程不会被回收,可以快速响应外界的确请求。

  2. CachedThreadPool
    通过 Executors 的 newCachedThreadPool 方法来创建,是一种线程数量不定的线程池,只有非核心线程,线程最大数为 Integer.MAX_VALUE ,超过 60 秒闲置线程会被回收,适合执行大量耗时较少的任务。

  3. ScheduledThreadPool
    通过 Executors 的 newScheduledThreadPool 方法来创建,它的核心线程数量是固定的,,非核心线程没有限制,当非核心线程闲置会被立即回收,只要适用于,执行定时任务和具有周期的重复任务。

  4. SingleThreadExecutor
    通过 Executors 的 newSingleThreadExecutor 方法来创建,线程池中只有一个核心线程,确保所有的任务都在同一个线程中按顺序执行,意义也在于此,使得任务之间不需要处理线程同步的问题。

出了上面的四种还可以根据需要灵活配置线程池。

載入中......
此文章數據所有權由區塊鏈加密技術和智能合約保障僅歸創作者所有。