Android 开发线程和线程池解析
线程分为主线程和子线程。主线程是指进程所拥有的线程(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. 统一所有的外界任务到一个线程中,使得任务之间不需要处理线程同步的问题。
上一篇: 生活总是给我们闹些冷笑话
下一篇: 有些人很会搞笑。