欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  移动技术

Android 开发线程和线程池解析

程序员文章站 2023-09-06 21:09:37
线程分为主线程和子线程。主线程是指进程所拥有的线程(java 默认情况下一个进程只有一个线程,这个线程就是主线程),主要处理和界面相关的事情,子线程执行耗时操作,子线程也叫工作线程。 asyncth...

线程分为主线程和子线程。主线程是指进程所拥有的线程(java 默认情况下一个进程只有一个线程,这个线程就是主线程),主要处理和界面相关的事情,子线程执行耗时操作,子线程也叫工作线程。 asyncthread封装了线程池和 handler,主要是为了方便开发者在子线程中更新ui。 handlerthread是一种具有消息循环的线程,内部可以使用 handler。 intentservice是一个服务,颞部采用 handlerthread 执行任务,当任务执行完毕后 intentservice 会自动退出。由于是四大之一,优先级比较高,不容易被后台杀死,保证任务的执行。 操作中,线程是操作系统调度的最小单元,同时线程又是一种受限的系统资源。线程不可能无限制产生,并且线程的创建和销毁都会有相应的开销。当系统中存在大量的线程,系统通过时间片轮转的方式调度每个线程,因此线程不可能做到绝对的并行,除非线程的数量小于cpu的数量,一般来说不可能。

11.2 android的线程形态

asynctask不适合执行特别耗时的后台任务,对于特别耗时的任务来说,建议使用线程池。因为如果特别耗时的话,可能 activity 都结束了,影响到 asynctask 的执行。 asynctask的类必须在主线程中加载,这就意味着第一次访问 asynctask 必须发生在主线程。 asynctask的对象必须在主线程中创建,execute 方法必须在ui线程调用。 不要在程序中直接调用onpreexecute、onpostexecute、onprogressupdate、doinbackground方法 一个 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 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. 统一所有的外界任务到一个线程中,使得任务之间不需要处理线程同步的问题。