分享会:Android线程和线程池源码解读

线程

操作系统调度最小单元,不可能无限制产生(受限的系统资源),线程创建与销毁有相应的开销。当系统中存在大量线程的时候,系统会通过时间片转轮的方式调度线程,因此线程不可能做到绝对运行。在进程中避免频繁创建销毁所带来的系统开销,就应该使用线程池。线程池会缓存一定数量的线程,Andoid线程池来自于java,主要通过Executor派生特定类型的线程池

注释:
时间片转轮调度算法:每个进程被分配一个时间段,称作它的时间片,即该进程允许运行的时间。如果在时间片结束时进程还在运行,则CPU将被剥夺并分配给另一个进程。如果进程在时间片结束前阻塞或结束,则CPU当即进行切换。调度程序所要做的就是维护一张就绪进程列表,当进程用完它的时间片后,它被移到队列的末尾。简单地说,线程组(ThreadGroup)就是由线程组成的管理线程的类。

特殊名词提前解释
ThreadGroup(线程组):

线程组ThreadGroup表示一组线程的集合,一旦一个线程归属到一个线程组之中后,就不能再更换其所在的线程组。那么为什么要使用线程组呢?个人认为有以下的好处:方便统一管理,线程组可以进行复制,快速定位到一个线程,统一进行异常设置等。ThreadGroup它其实并不属于Java并发包中的内容,它是java.lang中的内容。

线程组和线程池的区别:

二者最直观的区别是,线程池就像一台水轮机,它的叶片就像线程,可以循环不停,看起来只要水源不断,叶片就可以不停的周期性的工作。而线程组,只是管理一个或多个线程的Manager,而且它所管理的线程生命周期一旦结束,将永远停止,无法周期执行任务.

Future接口

Future接口代表异步计算的结果,通过Future接口提供的方法可以查看异步计算是否执行完成,或者等待执行结果并获取执行结果,同时还可以取消执行。

FutureTask

Future只是一个接口,不能直接用来创建对象,FutureTask是Future的实现类


FutureTask

FutureTask实现了RunnableFuture接口,则RunnableFuture接口继承了Runnable接口和Future接口,所以FutureTask既能当做一个Runnable直接被Thread执行,也能作为Future用来得到Callable的计算结果。

扮演线程角色的有:Thread,AsyncTask,IntentService,HandlerThread。(建议到此先看线程池)

AsyncTask

  1. 轻量级异步任务类(不适合特别耗时的任务),线程池执行后台任务,然后把进度和最终结果传给主线程更新UI

  2. 封装了线程池和 Handler ,主要是为了方便更新UI,由于多次改版原因,在不同api版本上有不同的表现

  3. THREAD_POOL_EXECUTOR:

    private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();
    private static final int CORE_POOL_SIZE = CPU_COUNT + 1;
    private static final int MAXIMUM_POOL_SIZE = CPU_COUNT * 2 + 1;
    private static final int KEEP_ALIVE = 1;
    
    public static final Executor THREAD_POOL_EXECUTOR
         = new ThreadPoolExecutor(CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE,
                 TimeUnit.SECONDS, sPoolWorkQueue, sThreadFactory);
    
    private static final BlockingQueue<Runnable> sPoolWorkQueue =
         new LinkedBlockingQueue<Runnable>(128);
    
  • 核心线程数 = CPU核心数 + 1
  • 最大线程数为CPU核心数的两倍 + 1
  • 核心线程无超时机制
  • 任务队列容量为128
  1. 源码解析笔记:

    1 . 源码中线程池有两个线程池(SerialExecutor 和 THREAD_POOL_EXECUTOR)和一个 Handler(InternalHandler),SerialExecutor 用于任务的排队,而THREAD_POOL_EXECUTOR用于真正地执行任务,InternalHandler用于将执行环境从线程池切换到主线程

    2 . AsyncTask 534行 execute方法:sDefaultExecutor为串行的线程池

    3 . AsyncTask 645行的finish方法为 628 行的finish方法,finish() 中调用了onPostExecute(Result result) 这个空方法

    4 . executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR,"")与execute(""),前者为并发,后者为串行,由 AsyncTask 源码590行exec.execute(mFuture);可知当线程池为默认线程池的时候是SerialExecutor implements Executor ,默认为串行,SerialExecutor真正的任务会一个一个的交给 THREAD_POOL_EXECUTOR 执行,而外层直接调用executeOnExecutor时传入THREAD_POOL_EXECUTOR ,那么任务就直接交给了THREAD_POOL_EXECUTOR 去执行(此线程池内部为并发)

HandlerThread

内部封装有looper的线程(上次分享会已讲)

IntentService

IntentService是一种服务,内部封装有HandlerThread和Handler,当任务执行完后IntentService会自动退出,作用很像一个后台线程,但是因为它是四大组件之一,所以进程优先级比线程高,不易被杀死,所以适用于高优先级的后台任务

startService(new Intent(this, LocalIntentService.class));就可以启动Service了(LocalIntentService.class为继承IntentService的实现类)。
在LocalIntentService类中,onHandleIntent()为处理Intent的方法。

源码解析 startService ->... -> onHandleIntent():

  1. startService() 方法为Context 的一个方法,调用 startService() 后,系统会回调 Service 类的 onCreate() 以及 onStart() 方法。这样启动的 Service 会一直运行在后台,直到 Context.stopService() 或者 selfStop() 方法被调用。另外如果一个 Service 已经被启动,其他代码再试图调用 startService() 方法,是不会执行 onCreate() 的,但会重新执行一次 onStart() 。

  2. onCreate() :先创建一个 HandlerThread 对象,然后使用他的 looper 来构造一个Handler 对象 mServiceHandler(这样通过 mServiceHandler 发送的消息都会在IntentService 中执行)。此时更像是完成了初始化工作

  3. onStartCommand():每次启动 IntentService,他的 onStartCommand 方法就会调用一次,而 onStartCommand方法里面又每次都会调用 onStart,注意这个方法不要重写

  4. onStart() :这个方法将 Intent 封装成了一个Message,并通过 mServiceHandler 发送到 HandlerThread 中处理。注意 mServiceHandler 绑定的 looper 就是HandlerThread 的looper,所以最后会调用 mServiceHandler 的 handleMessage(Message msg) 处理后台任务了。

  5. ServiceHandler 类:

    @Override
     public void handleMessage(Message msg) {
         onHandleIntent((Intent)msg.obj);
         stopSelf(msg.arg1);
     }
    

最主要的就是这两行:可以看见 onHandleIntent((Intent)msg.obj) 把传递到这里的交给了我们最外层看见的 onHandlerIntent()。同时也说明按顺序处理消息(因为 Handler 中的 looper 是按顺序执行后台任务)

  1. onHandlerIntent 执行完毕后,源码中通过 stopSelf(int startId)尝试停止服务
    stopSelf() 会直接停止服务,而 stopSelf(int startId)会等消息处理完才停止

线程池

真正的线程池的实现为ThreadPoolExecutor。ThreadPoolExecutor提供一系列参数配置线程池。

线程池的好处:

  • 重用,节省性能开销
  • 控制线程池并发数,避免大量线程相互抢占系统资源而导致的阻塞现象
  • 能进行简单管理,提供定时执行及指定间隔循环执行等功能

专业名词提前解释(构造函数中的主要参数)

  1. corePoolSize
  2. maxmumPoolSize
  3. keepAliveTime
  4. unit:keepAliveTime 的时间单位
  5. workQueue:线程池任务队列,通过线程池的execute 方法提交的 Runnable 对象会储存在这个参数中
  6. threadFactory:线程工厂,为线程池提供创建新线程的功能。为接口。

线程ThreadPoolExecutor源码简单介绍
核心线程:

Executor与Executors介绍

Android的线程池主要分为四种:

  1. FixedThreadPool
  2. CachedThreadPool
  3. ScheduledThreadPool
  4. SingleThreadPool

FixedThreadPool :

能复用 固定数量的线程 处理一个 共享的无边界队列 。线程处于空闲时不会被收回。如果当所有线程都是活动时,有多的任务被提交过来,那么它会一致在队列中等待直到有线程可用。如果任何线程在执行过程中因为错误而中止,新的线程会替代它的位置来执行后续的任务。所有线程都会一致存于线程池中,直到显式的执行 ExecutorService.shutdown() 关闭。
线程池中只有核心线程且不会被收回,意味着能快速响应外界请求
此核心线程没有超时机制

CachedThreadPool:

CachedThreadPool 是通过 java.util.concurrent.Executors 创建的 ThreadPoolExecutor 实例。这个实例会根据需要,在线程可用时,重用之前构造好的池中线程。这个线程池在执行 大量短生命周期的异步任务时(many short-lived asynchronous task),可以显著提高程序性能。调用 execute 时,可以重用之前已构造的可用线程,如果不存在可用线程,那么会重新创建一个新的线程并将其加入到线程池中。如果线程超过 60 秒还未被使用,就会被中止并从缓存中移除。因此,线程池在长时间空闲后不会消耗任何资源。
核心线程数为0
适用于大量耗时操作

ScheduledThreadPool:

核心线程数量固定,非核心线程数量没有限制,当非核心线程闲置时会被立即收回。
ScheduledThreadPool主要用于执行定时任务和具体固定周期的重复任务

SingleThreadPool:

线程池内部只有一个核心线程,确保所有任务都在一个线程中按顺序执行。和 FixedThreadPool的区别在于,如果线程遇到错误中止,它是无法使用替代线程的。
统一所有外界任务到一个线程中,使这些任务不需要处理同步问题

四种线程池的实现及实现源码
Executors -> ... ->ThreadPoolExecutor

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

推荐阅读更多精彩内容

  • Android中的线程 线程,在Android中是非常重要的,主线程处理UI界面,子线程处理耗时操作。如果在主线程...
    shenhuniurou阅读 748评论 0 3
  • 简介 1. 线程分类 主线程(UI线程) : 处理和界面相关的事情. 子线程 : 处理耗时操作. Android中...
    王世军Steven阅读 908评论 0 2
  • 从用途上来说,线程分为主线程和子线程,主线程主要处理和界面相关的事情,子线程则往往用于执行耗时操作。 除了Thre...
    小柏不是大白阅读 620评论 0 3
  • 11.1 主线程和子线程 Android中的主线程主要处理和界面相关的事情,而子线程则往往用于执行耗时操作,如网络...
    Xerrard阅读 369评论 0 0
  • 这个机构坐落远离市区的地方,红白房子欧式风格,周围绿树环绕,少了喧嚣多了一份宁静。会来这儿的家庭都是被自闭...
    阿酱子阅读 247评论 0 1