天天看点

第十一章 Android 的线程和线程池

  1. 线程分为主线程和子线程。主线程是指进程所拥有的线程(java 默认情况下一个进程只有一个线程,这个线程就是主线程),主要处理和界面相关的事情,子线程执行耗时操作,子线程也叫工作线程。
  2. AsyncThread

    封装了线程池和 Handler,主要是为了方便开发者在子线程中更新UI。
  3. HandlerThread

    是一种具有消息循环的线程,内部可以使用 Handler。
  4. IntentService

    是一个服务,颞部采用 HandlerThread 执行任务,当任务执行完毕后 IntentService 会自动退出。由于是四大组件之一,优先级比较高,不容易被后台杀死,保证任务的执行。
  5. 操作系统中,线程是操作系统调度的最小单元,同时线程又是一种受限的系统资源。线程不可能无限制产生,并且线程的创建和销毁都会有相应的开销。当系统中存在大量的线程,系统通过时间片轮转的方式调度每个线程,因此线程不可能做到绝对的并行,除非线程的数量小于CPU的数量,一般来说不可能。

11.2 Android的线程形态

  1. AsyncTask

    不适合执行特别耗时的后台任务,对于特别耗时的任务来说,建议使用线程池。因为如果特别耗时的话,可能 Activity 都结束了,影响到 AsyncTask 的执行。
  2. AsyncTask

    的类必须在主线程中加载,这就意味着第一次访问 AsyncTask 必须发生在主线程。
  3. AsyncTask

    的对象必须在主线程中创建,execute 方法必须在UI线程调用。
  4. 不要在程序中直接调用

    onPreExecute、onPostExecute、onProgressUpdate、doInBackground

    方法
  5. 一个

    AsyncTask

    对象只能执行一次,即只能调用一次

    execute

    方法,否则会报运行时异常。

    工作原理:

    1: 首先系统会把

    AsyncTask

    的 Params 参数封装成

    FutureTask

    对象,

    FutureTask

    是一个并发类,这里充当了 Runnable 的作用。

    2: 接着

    FutureTask

    就会交给

    SerialExecutor

    的 execute 方法去处理,

    SerialExecutor

    的 execute 方法首先把

    FutureTask

    插入到任务队列 mTasks 中去,如果此时没有正在活动的 AsyncTask 任务,那么就会调用

    SerialExecutor

    scheduleNext

    方法通过线程池

    THREAD_POOL_EXECUTOR

    来执行下一个

    AsyncTask

    任务。同时当一个

    AsyncTask

    任务执行完后,就会继续执行其他任务知道所有的任务都被执行为止(默认情况下,AsyncTask 是串行执行的)

    3: 执行任务后,通过

    InternalHandler

    将执行环境切换到主线程。

    介绍

    SerialExecutor

    :用于任务的排队。

    THREAD_POOL_EXECUTOR

    :用于真正地执行任务。

    InternalHandler

    :用于将执行环境从线程池切换到主线程。

11.3 HandlerThread

实现:在 run 方法中通过

Looper.prepare()

来创建消息队列,并通过

Looper.loop()

来开启消息循环,这样就允许在HandlerThread 中创建 Handler 了。

使用场景:

IntentService

。由于HandlerThread 的 run 方法是一个无限循环,因此当明确不再使用 HandlerThread 的时候,可以通过 quit 或者 quitSafely 来终止线程的执行。

11.4 IntentService

IntentService

是一个抽象类,继承了 Service。IntentService 可以执行后台耗时的任务,当任务执行后他会自动停止,同时由于 IntentService 是服务的原因,导致优先级比单纯的线程高很多,不容易被系统杀死。

IntentService

被第一次启动时,他的 onCreate 方法会被调用。

public void onCreate() {
        super.onCreate();
        /**
         * 创建一个HandlerThread对象,HandlerThread是Thread,不是Handler
         */
        HandlerThread thread = new HandlerThread("IntentService[" + mName + "]");
        thread.start();
        /**
         * 获取在HandlerThread的run方法中创建的Looper对象,当有消息的时候,就会调用Handler的dispatchMessage方法分发消息给特定的方法执行
         *
         *    在该类,消息队列一有消息,Looper就会调用ServiceHandler的dispatchMessage方法,
         * 然后调用handleMessage(),而这个过程就是在HandlerThread的run()中执行的(因为Looper.looper会调用Handler的dispatchMessage方法)
         */
        mServiceLooper = thread.getLooper();
        /**
         * 根据HandlerThread对象中的Looper对象创建ServiceHandler对象
         *
         * ServiceHandler继承了handler
         * mServiceHandler发送的消息在HandlerThread中执行(因为用的是HandlerThread中的Looper)
         */
        mServiceHandler = new ServiceHandler(mServiceLooper);
    }
           

每次启动

IntentService

onStartCommand

会被调用,

onStartCommand

调用

onstart

public void onStart(Intent intent, int startId) {
        Message msg = mServiceHandler.obtainMessage();
        msg.arg1 = startId;
        msg.obj = intent;
        mServiceHandler.sendMessage(msg);
    }
           

原理

IntentService

通过

ServiceHandler

发送一个消息到消息队列,而消息队列是在

HandlerThread

中创建的,在

HandlerThread

中由于消息循环,

Looper.loop()

将消息取出来,并调用

ServiceHandler

dispatchMessage

方法,然后调用

IntentService

handleMessage

方法,在该方法中调用了

onHandleIntent

方法和stopSelf(int startId)方法,这样

startService

中的intent就到了这里。

onHandleIntent

是一个抽象方法,需要我们在子类中实现,并能通过 intent 区别任务。如果当前只存在一个后台任务,那么

onHandleIntent

方法执行完这个任务之后,

stopSelf(int startId)

就会直接停止服务;如果目前存在多个后台任务,那么当

onHandleIntent

执行完最后一个任务时,

stopSelf(int startId)

才会直接停止服务。

由于每执行一个后台任务就必须启动一次

IntentService

,而

IntentService

内部则通过消息的方式向

HandlerThread

请求执行任务,Handler 中的 Looper 是顺序处理消息的,这就意味着 IntentService 也是顺序执行后台任务的,当有多个任务同时存在,这些任务会按照外界发起的顺序排队执行。

stopSelf() 和 stopSelf(int startId)

stopSelf()

:会立刻停止服务,此时可能还有其他消息未处理。

stopSelf(int startId)

:在尝试停止服务之前会判断最近启动服务的次数是否和 startId 相等,如果相等就立刻停止服务,不相等不停止服务。

11.5 Android 的线程池

线程池的优点

1. 重用线程池的线程,避免因为线程的创建和销毁带来的性能开销

2. 能有效控制线程池的最大并发数,避免大量线程因抢占系统资源而导致的阻塞现象。

3. 能够对县城进行简要管理,并提供定时执行以及间隔循环执行等功能。

ThreadPoolExecutor

ThreadPoolExecutor

是线程池的真正实现,提供了一系列参数配置线程池。

/**
     * 核心线程数   默认情况下,核心线程会在线程池中一直存活,即使处于闲置状态。
     */
    private volatile int corePoolSize;
    /**
     * 线程池所能容纳的最大线程数,当活动线程数达到这个数值之后,后续的新任务将会被阻塞
     */
    private volatile int maximumPoolSize;
    /**
     * 如果将该属性置为true,闲置的核心线程等待新任务到来的时候会有超时策略,时间间隔由keepAliveTime决定
     *
     * 当等待时间超过keepAliveTime指定的时间之后,核心线程就会停止
     */
    private volatile boolean allowCoreThreadTimeOut;
    /**
     * 当allowCoreThreadTimeOut不为true的时候,非核心的线程闲置的超时时长,超过这个时长,非核心线程就会回收
     *
     * 当allowCoreThreadTimeOut为true的时候,同样作用于核心线程。
     */
    private volatile long keepAliveTime;
    /**
     * 线程工厂,为线程池提供创建新线程的功能,
     *
     * ThreadFactory是一个接口,只有一个方法newThread(Runnable r)
     */
    private volatile ThreadFactory threadFactory;
    /**
     * 线程池中的任务队列,通过线程池的execute方法提交的Runnable对象会存储在这个参数中
     */
    private final BlockingQueue<Runnable> workQueue;
    /**
     *      当线程池无法执行任务的时候,这可能是由于任务队列已满或者是无法成功执行任务,
     *  此时ThreadPoolExecutor会调用RejectedExecutionHandler的rejectedExecution
     *  方法通知调用者,默认情况下抛出RejectedExecutionException异常。
     */
    private volatile RejectedExecutionHandler handler;
           

ThreadPoolExecutor 执行任务的规则

1. 如果线程池中的线程数量未达到核心线程数,那么会直接启动一个核心线程来执行任务。

2. 如果线程池中的线程数量已经达到或者数量超过核心线程的数量,那么任务就会被插入到任务队列中排队等待执行。

3. 如果步骤 2 中无法将任务插入都任务队列中,这往往是由于任务队列已满,这个时候如果线程数量未达到线程池规定的最大值,那么会立即启动一个非核心线程来执行任务。

4. 如果步骤3中线程数量已经达到线程池规定的最大值,那么就拒绝执行此任务,此时

ThreadPoolExecutor

会调用

RejectedExecutionHandler

rejectedExecution

方法通知调用者。

11.6 线程池的分类

FixedThreadPool:

1.只有核心线程,且线程数量固定当线程处于空闲状态

2.不会被回收,除非线程池被关闭了

3.任务队列没有限制

CachedThreadPool

1. 核心线程数为0,非核心线程数量不固定,最大线程数可以任意大

2. 线程池中的空闲线程有超时机制,为60秒

ScheduledThreadPool

1. 核心线程数量固定,非核心线程数量没有限制

2. 非核心线程闲置的时候会被立即回收

3. 主要用于执行定时任务和具有固定周期的重复任务

SingleThreadExecutor

1. 只有一个核心线程,确保所有的任务都在同一个线程中按顺序执行

2. 统一所有的外界任务到一个线程中,使得任务之间不需要处理线程同步的问题

继续阅读