Android线程和线程池


image.png

主线程与子线程

  1. 主线程:主线程主要处理UI交互,需要在任何时候都有较高的响应速度,否则界面会产生卡顿。主线程中不能执行耗时任务,否则会出现ANR。主要作用是运行四大组件,处理它们和用户的交互
  2. 子线程:也叫工作线程,除主线程以外的线程都叫子线程。主要用来处理耗时任务,如网络请求、I/O。

Android中的线程形态

Android中除了传统的Thread之外,还包含了AsyncTask、HandlerThread以及IntentService,这三者底层实现也是Thread,但它们有特殊的形态和各自的优缺点。

  1. AsyncTask
  • 一种轻量级的异步任务类,可以在线程池中执行后台任务,然后把进度和结果传递给主线程,并在主线程中更新UI。AsyncTask内部封装了Thread和Handler。
  • AsyncTask是一个抽象泛型类,提供了三个参数Params、Progress和Result,Params表示参数,Progress表示后台任务执行的进度类型,Result表示返回结果的类型。
public abstract class AsyncTask<Params, Progress, Result> 
  • 四个核心方法:onPreExecute=异步任务执行之前调用,可做一些准备工作(主线程)、doInBackground=用于执行异步任务(线程池),在此方法中调用publishProgress可以更新任务进度onProgressUpdate=后台任务进度发生改变会调用(主线程)、onPostExecute=异步任务执行完成之后,该方法被调用(主线程)
  • 除了上面的四个方法,AsyncTask还提供了onCancelled方法(主线程),异步任务取消时,onCancelled会被调用,这个时候onPostExecute不会被调用。
  • AsyncTask使用过程中有几个限制:AsyncTask必须在主线程中创建和加载,execute方法也要在主线程中调用,execute方法只能调用一次,不然会报错。不要在程序中直接调用上面四个核心方法。
  • version < Android1.6:串行执行任务,Android1.6 <= version < Android3.0:采用线程池并行执行任务,version >= Android3.0:线程池串行执行任务,不过仍可通过executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, xxx)指定并发线程池来并行执行任务
  • AsyncTask原理分析:1、AsyncTask的构造方法中会初始化一个sHandler:InternalHandler对象,还会初始化一个mWorker:WorkerRunnable对象(执行doInBackgroud,并且把结果发送个sHandler),还有会用mWorker初始化mFuture:FutureTask对象(可取消、可查询任务是否完成,可获取任务结果)。2、execute方法会默认使用sDefaultExecute(串行线程池)来调用executeOnExecutor方法,这个方法中会调用onPreExecute,并且会把mFuture作为一个Runnable(任务)插入线程池的队列mTask。3、如果此时没有active的AsyncTask任务,就会调用scheduleNext来执行队列中的下一个AsyncTask,直到所有任务被执行完为止。4、mFuture任务执行完成后会发送消息给sHandler对象,sHandler处理消息时调用onPostExecute把结果传递给主线程处理
  1. HandlerThread
  • 继承自Thread,可以使用Handler的Thread
  • 在run方法中通过Looper.prepare初始化Looper,并且通过Looper.loop方法开启消息循环。这样就允许在HandlerThread中使用Handler了。
  • 普通Thread在run方法中执行耗时任务。而HandlerThread在run中开启消息循环,外界需要通过Handler发送消息通知HandlerThread执行一个具体任务。
  • 由于HandlerThread方法中调用Looper.loop方法,是一个死循环,当不需要再使用HandlerThread时,通过quit或quitSafety方法终止线程执行。
  1. IntentService
  • 是一种特殊的Service,继承Service的抽象类,抽象方法是onHandleIntent。正常的Service运行在主线程,所以不能执行耗时任务,而IntentService的任务是放在HandlerThread中执行,因此可以执行耗时任务。并且IntentService是四大组件,所以相对于一般线程来说,不易被系统杀死。
  • 在onCreate方法中,创建一个HandlerThread线程,然后用该线程的Looper构造一个Handler(即该Handler是在HandlerThread线程中处理消息)。
  • 每次启动IntentService(startService),它的onStartCommand方法会被调用,onStartCommand会调用onStart方法,onStart方法中会把Intent作为消息参数,发送一个msg给Handler处理,而Handler处理中会调用onHandleIntent方法(运行在在HandlerThread线程中)。
  • Handler处理消息后还会调用stopSelf(msg.arg1)方法结束IntentService,如果目前只存在一个后台任务,那么执行完后stopSelf直接停止服务,如果有多个后台任务,那么当onHandleIntent执行完最后一个任务时,stopSelf才会停止服务。
  • onHandleIntent执行多个后台任务,是按照外界发起的顺序一次执行。

Android中的线程池

  1. 线程池的优点
  • 可以复用线程池中的线程,减少创建和销毁线程带来的开销
  • 有效控制线程池中最大并发数,避免线程之间相互抢占资源导致阻塞现象
  • 对线程进行管理,提供定时和周期执行功能
  1. ThreadPoolExecutor
    ThreadPoolExecutor的继承关系如下:
Executor interface
ExecutorService interface
AbstractExecutorService abstract class
ThreadPoolExecutor class

构造方法如下:

public ThreadPoolExecutor(int corePoolSize,
                          int maximumPoolSize,
                            long keepAliveTime,
                            TimeUnit unit,
                            BlockingQueue<Runnable> workQueue,
                            ThreadFactory threadFactory,
                            RejectedExecutionHandler handler) {
      if (corePoolSize < 0 ||
          maximumPoolSize <= 0 ||
          maximumPoolSize < corePoolSize ||
          keepAliveTime < 0)
          throw new IllegalArgumentException();
      if (workQueue == null || threadFactory == null || handler == null)
          throw new NullPointerException();
      this.corePoolSize = corePoolSize;
      this.maximumPoolSize = maximumPoolSize;
      this.workQueue = workQueue;
      this.keepAliveTime = unit.toNanos(keepAliveTime);
      this.threadFactory = threadFactory;
      this.handler = handler;
  }

构造方法中的参数解释:

  • corePoolSize:核心线程数,核心线程在线程池中能够一直存活,及时处于闲置状态。除非把allowCoreThreadTimeOut设置为true,那么当核心线程闲置超过一定时间,就会被终止。
  • maximumPoolSize:线程池能容纳的最大线程数,当线程数达到这个值,后续的任务将会被阻塞。
  • keepAliveTime:线程闲置超时时间,正常情况下,非核心线程闲置超过这个时间会被回收,当设置了allowCoreThreadTimeOut,那么该参数对核心线程也生效。
  • unit:keepAliveTime对应的单位,TimeUnit中取值
  • workQueue:线程池中的任务队列,通过线程池的execute方法提交的Runnable都会存储在此。
  • threadFactory:线程工厂,为线程池提供创建线程的功能,是个接口,只有一个方法:Thread newThread(Runnable r)
  • handler:不常用参数,是RejectedExecutionHandler类型,当线程池无法执行新任务时(任务队列已满),这是会调用handler的rejectedExecution来通知调用者,默认情况会抛出一个异常RejectedExecutionException

ThreadPoolExecutor执行逻辑如下:

1. 线程池线程数量未达到核心线程数,直接启动核心线程执行任务
2. 如果达到或超过核心线程数,会插到任务对队列中排队等待执行
3. 任务队列已满,如果线程数量未达到最大线程数,则会启动非核心线程执行任务
4. 如果达到最大线程数,就会拒绝新任务,会调用RejectedExecutionHandler的rejectedExecution来通知调用者
  1. 线程池分类
    主要介绍四种常见的线程池,都直接或间接配置ThreadPoolExecutor实现自己的功能。
  • FixedThreadPool:线程数量固定,都是核心线程,任务队列无限大。线程不会被回收,能够快速响应外界请求
  • CachedThreadPool:都是非核心线程,并且非核心线程数量无上限,闲置超时时间为60秒,任务队列为空集合,所有任务都能立刻执行。所有线程处于闲置状态时,就会出现线程池内无线程的情况。
  • ScheduledThreadPool:核心线程固定,非核心线程无上限,主要用来执行定时任务和周期任务。
  • SingleThreadExecutor:只有一个核心线程,所有任务在核心线程中顺序执行,不需要处理线程同步问题。

总结

本文主要介绍了Android中线程存在形式,还有线程池的分类和使用。希望对大家有帮助。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,591评论 6 501
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,448评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,823评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,204评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,228评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,190评论 1 299
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,078评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,923评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,334评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,550评论 2 333
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,727评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,428评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,022评论 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,672评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,826评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,734评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,619评论 2 354

推荐阅读更多精彩内容