第十一章 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 任务,那么就会调用SerialExecutorscheduleNext方法通过线程池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);
    }

每次启动 IntentServiceonStartCommand会被调用,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()将消息取出来,并调用ServiceHandlerdispatchMessage方法,然后调用IntentServicehandleMessage方法,在该方法中调用了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会调用RejectedExecutionHandlerrejectedExecution 方法通知调用者。
11.6 线程池的分类
FixedThreadPool
1.只有核心线程,且线程数量固定当线程处于空闲状态
2.不会被回收,除非线程池被关闭了
3.任务队列没有限制
CachedThreadPool
1. 核心线程数为0,非核心线程数量不固定,最大线程数可以任意大
2. 线程池中的空闲线程有超时机制,为60秒
ScheduledThreadPool
1. 核心线程数量固定,非核心线程数量没有限制
2. 非核心线程闲置的时候会被立即回收
3. 主要用于执行定时任务和具有固定周期的重复任务
SingleThreadExecutor
1. 只有一个核心线程,确保所有的任务都在同一个线程中按顺序执行
2. 统一所有的外界任务到一个线程中,使得任务之间不需要处理线程同步的问题

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值