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

概述

  1. 从用途来说,线程分为主线程和子线程,主线程主要处理和界面相关的事情,而子线程则往往用于耗时操作。
  2. 在操作系统中,线程是操作系统调度的最小单元,同时线程又是一种受限的系统的资源,并且线程的创建和销毁都有相应的开销。
  3. 一个线程池会缓存一定数量的线程,通过线程池就可以避免因为频繁替换和销毁线程所带来的系统开销。Android中的线程池来源于Java,主要是通过Executor来派生特定类型的线程池。

1. 主线程和子线程

  1. 主线程是指进程所拥有的线程。主线程主要处理界面交互相关的逻辑,不能处理耗时的任务。除了主线程以外的线程都是子线程。

2. Android中的线程形态

除了传统的Thread以外,还包含AsyncTask、HandlerThread以及IntentService,这三者的底层实现也是线程。

1. AsyncTask:
  1. 从实现上来说,AsyncTask封装了Thread和Handler。
  2. AsyncTask的类必须在主线程中加载,这就意味着第一次访问AsyncTask必须发生在主线程,
  3. AsyncTask的对象必须在主线程中创建。
  4. execute方法必须在UI线程调用。
2.AsyncTask的工作原理:
  1. 首先系统会把AsyncTask的Params参数封装为FutureTask对象,FutureTask是一个并发类,充当了Runnable的作用。
  2. SerialExecutor的execute方法首先会吧FutureTask对象插入到任务队列中。如果此时没有正在活动的任务,那么就会执行下一个AsyncTask任务。同时一个任务执行完成后,会继续执行下一个,直到所有的任务执行完成。
  3. AsyncTask有两个线程池SerialExecutor和THREAD_POOL_EXECUTOR和一个handler(InternalHandler)。SerialExecutor用于任务的排队,THREAD_POOL_EXECUTOR用于真正的执行任务。InternalHandler用于将执行环境从线程池切换到主线程。
  4. FeatureTask对象会间接调用AsynTask的doInBackGroud方法,将结果传递给postResult。
  5. postResult会通过sHandler发送消息。sHandler是一个静态的Handler对象,sHandler对象必须在主线程中创建。
  6. sHandler收到MESSAGE_POST_RESULT消息后会调用AsyncTask的finish方法。
3. HandlerThread:
  1. HandlerThread继承了Thread,它是一种可以使用Handler的Thread,它的实现方式是,在run方法中通过Looper.prepare()来创建消息队列,并通过Looper.loop()来开启消息循环。
  2. 它和普通的Thread的区别是:普通Thread主要用于在run方法中执行一个耗时任务,HandlerThread在内部创建了消息队列,外界需要通过Handler的消息方式来通知HandlerThread执行一个具体的任务。一个具体的使用场景是IntentService。HandlerTherad的run方法是一个无限循环,因此确定不需要时,通过它的quit或者quitSafely方法来终止线程的执行。
4. IntentService
  1. IntentService是一种特殊的Service,它继承了Service并且它是一个抽象类,因此必须创建它的子类才能使用。
  2. IntentService第一次启动时,会创建HandlerThread,然后通过Looper构建一个Handler对象mServiceHandler,这样通过mServiceHandler发送的消息最终都会在HandlerThread中执行。
  3. mServiceHandler收到消息后,会将Intent传递给onHandleIntent方法去处理。onHandleIntent是一个抽象方法,它需要我们在子类中实现。
  4. Handler中的Looper是顺序处理消息的,意味着IntentService也是顺序执行后台任务的。

3. Android中的线程池

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

1. ThreadPoolExecutor
  1. ThreadPoolExecutor是线程池真正的实现,构造方法提供了一些列参数来配置线程池。

    public ThreadPoolExecutor(            int corePoolSize,            int maximumPoolSize,            long keepAliveTime,            TimeUnit unit,            BlockingQueue<Runnable> workQueue,            ThreadFactory threadFactory)
    1. corePoolSize:
      线程池的核心线程数,默认情况下,核心线程会在线程池中一直存活,即使它们处于闲置状态。如果将ThreadPoolExecutor的allowCoreThreadTimeOut属性设置为true,那么闲置的核心线程在等待新任务到来时会有超时策略,这个时间间隔有keepAliveTime所指定,当等待时间超过keepAliveTime所指定的时常后,核心线程就会被终止。
    2. maximumPoolSize:
      线程池所能容纳的最大线程数,当活动线程数达到这个数值后,后序的新任务会被阻塞。
    3. keepAliveTime:
      非核心线程闲置时的超时时常,超过这个时常,非核心线程就会被回收。当ThreadPoolExecutor的allowCoreThreadTimeOut属性设置为true时,keepAliveTime同样会作用于核心线程。
    4. unit:
      用于执行keepAliveTime参数的时间单位,这是一个枚举,常用的TimeUnit.MILLISECONEDS(毫秒)、TimeUnit.SECONDS(秒)以及TimeUnit.MINUTES(分钟)。
    5. workQueue:
      线程池中的任务队列,通过线程池的execute方法提交的runnable对象会存储在这个参数中。
    6. threadFactory:
      线程工厂,为线程池提供创建新线程的功能。
    7. RejectedExecutionHandler :
      不常用,线程池无法执行新任务时,可能是由于任务队列已满或者是无法成功执行任务。会调用handler的rejectedExecution方法来通知调用者。
  2. ThreadPoolExecutor执行任务的大致顺序:

    1. 如果线程池中的线程数量未达到核心线程的数量,那么会直接启动一个核心线程来执行任务。
    2. 如果线程池中的线程数量已经达到了或者超过了核心线程的数量,那么任务会被插入到任务队列中排队等待执行。
    3. 如果在步骤2中无法将任务队列插入到任务队列中,这往往是由于任务队列已满,这个时候如果线程数量未达到线程池规定的最大值,那么会立刻启动一个非核心线程来执行。
    4. 如果步骤3中的线程数量已经达到线程池规定的最大值,那么就拒绝此任务,ThreadPoolExecutor会调用RejectedExecutionHandlerrejectedExecution方法来通知调用者。
  3. AsyncTask的THREAD_POOL_EXECUTOR线程池的配置:

    1. corePoolSize=CPU核心数+1;
    2. maximumPoolSize=2倍的CPU核心数+1;
    3. 核心线程无超时机制,非核心线程在闲置时间的超时时间为1s;
    4. 任务队列的容量为128。
  4. Android中常见的4类具有不同功能特性的线程池:

    1. FixedThreadPool:线程数量固定的线程池,它只有核心线程;
    2. CachedThreadPool:线程数量不固定的线程池,它只有非核心线程;
    3. ScheduledThreadPool:核心线程数量固定,非核心线程数量没有限制的线程池,主要用于执行定时任务和具有固定周期的任务;
    4. SingleThreadPool:只有一个核心线程的线程池,确保了所有的任务都在同一个线程中按顺序执行。

更多相关文章

  1. Android中的线程模型,
  2. [转载]Android中的线程模型
  3. android异步线程利用Handler将消息发送至UI线程
  4. Android消息机制原理,仿写Handler Looper源码跨线程通信原理--之
  5. Android核心分析 之一--------分析方法论探讨之设计意图
  6. Android UI 线程更新UI也会崩溃???
  7. Android创建子线程和回调主线程的几种方式
  8. android 照个图 要开线程 不然永远拍的图都是那么的小

随机推荐

  1. 慎用原生MediaPlayer类播放音频
  2. Android(安卓)JNI编程(七)——使用AndroidS
  3. android 机器耗电
  4. Android(安卓)基础总结:( 十六)Android(安卓
  5. C#+Hybrid App(Android)实现微信APP支付
  6. Android仿微信图片编辑处理:文字,马赛克,裁
  7. Android(java)学习笔记95:Android原理揭秘系
  8. Android(安卓)启动过程框架
  9. 5分钟实现Android最新2018友盟统计
  10. Android(安卓)5.0 开机横屏修改方法