摘要:源码分析带着疑问,打开源码看看,原来里有两个执行器,一个用于串行执行,一个用于并行执行,这不就是我们熟悉的里的线程池嘛。
前言
今天项目经理给无(jing)所(pi)事(li)事(jin)的我安排了一个小任务,要在他的专属阅读APP进入首页后加载一张网络上的美女图片,说这样才能安心看书,恩,这还不简单,我第一时间想到了AsyncTask。
实现protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); /*...... 此处省略1万行代码 ......*/ BeautifulGirlTask girlTask = new BeautifulGirlTask(); girlTask.execute(); /*...... 此处省略2万行代码 ......*/ }
搞定,坐等升职加薪迎娶白富美喽。
第二天,项目经理说有时美女图片加载很慢,过很久才出来,严重影响了他学习。学你妹,仔细查了查代码我发现其他同学也为项目经理写了很多AsynTask小任务,难道这之间有什么影响吗。
首先,先写一个用于测试的AsyncTask, doInBackground方法中让线程休眠1秒,来模拟任务执行,并且在任务开始执行时打印出任务id和线程id,
public class TestTask extends AsyncTask{ private int id; public TestTask(int id) { this.id = id; } @Override protected Void doInBackground(Void... params) { Log.d("morven","morven--- task-"+id+" start"+",tid="+Thread.currentThread().getId()); try { Thread.sleep(1000); } catch (InterruptedException e) { Log.e("TAG", "sleep interrupt"); } return null; } }
然后,循环创建18个任务并执行,
for (int i=0; i<18; i++) { TestTask task = new TestTask(i); task.execute(); }
来看看执行结果:
每个任务的启动时间都间隔一秒,说明任务执行是线性的,恩,这么一说我想起来某某大牛的blog里说AsyncTask的执行可以是线性的也可以是并发的;从线程id来看,从第9个任务开始都是使用的同一个线程。
再来试试并发执行,我们将task创建并执行的代码改成下面这样,顺便将cpu核心数也打印出来:
int CPU_COUNT = Runtime.getRuntime().availableProcessors(); Log.d("morven","morven--- CPU_COUNT="+CPU_COUNT); for (int i=0; i<18; i++) { TestTask task = new TestTask(i); task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); }
看看结果:
8核手机就是牛,从日志里看task0-8都是在25:59同时触发执行的,task9-17都是在1秒后同时触发执行,观察线程id也能看出,后面9个task重用了前面9个task的线程,噢,线程池里有9个线程,这跟8核有什么关系吗。
源码分析带着疑问,打开源码看看,
原来AsyncTask里有两个执行器,一个用于串行执行,一个用于并行执行,这不就是我们熟悉的java.concurrent里的线程池嘛。
先看并行执行的ThreadPoolExecutor,它的前两个参数CORE_POOL_SIZE和MAXIMUM_POOL_SIZE分别定义了线程池的核心线程数和最大线程数,sPoolWorkQueue提供了线程池的工作队列,线程池运行的规则如下:
(1)一个任务被提交给线程池,如果当前线程池中线程数量小于核心线程数(CORE_POOL_SIZE),那么创建一个新的线程去执行任务。
(2)如果任务被提交给线程池后,线程池中线程数量已等于核心线程数,那么看核心线程中是否有空闲线程,如果有则用空闲线程执行任务,否则将任务放入工作队列等待。
(3)如果任务提交后,没有空闲核心线程且工作队列也满了,那么判断当前核心线程数目是否小于最大核心线程数(MAXIMUM_POOL_SIZE),如果小于,那么创建一个新的线程作为核心线程执行该任务。
在源码中,我们看到,CORE_POOL_SIZE被赋值为CPU核心数+1,MAXIMUM_POOL_SIZE赋值为CPU核心数×2+1,
所以在前面的测试中,AsyncTask创建了9个核心线程,一次可并发执行9个任务。
工作队列的大小为128,
那么在我的机器上,如果一次提交9+128=137个任务,且这些任务都未执行完,那么工作队列将被放满,后续再提交的(2×8+1=17)-9 = 8个任务将会在新的线程中执行。
我们将任务的sleep时间改为100秒,创建200个task看看:
for (int i=0; i<200; i++) { TestTask task = new TestTask(i); task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); }
try { Thread.sleep(100000); } catch (InterruptedException e) { Log.e("TAG", "sleep interrupt"); }
看看结果:
线程池开始创建了9个线程去运行task0-8,后面提交的task都被放入工作队列中了,直到队列中放满128个task,当第138个任务(task-137)提交执行时,线程池创建了新的线程去执行,一共创建了8个,咦,看图中怎么是9个,有个tid=1的线程,恩,当工作队列已满且无法创建新线程时,任务将会被交给handler执行,也就是主线程,所以我们的UI已经卡死不能响应了。
结束语AsyncTask给开发带来了极大的方便,但作为小白,总觉得交给别人管理的东西总是行为古怪,除非了解管理的机制。oh,我可不想做回原始人哦。
恩,元旦来啦,祝大家开(jia)心(ban)~
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/12490.html
摘要:如果某个的实例并不处于栈顶的话,系统将会创建新的实例,和一样。最后再看看,只有被重新压入了栈顶,仍在栈的底部,这样也满足要求。 前言 新年伊始,本打算大展宏图的本人却一直处于项目经理的忽视之中,终于的终于,本人迎来了新年的第一个重磅需求。作为一个拥有上亿用户的APP,本APP的用户条款竟然未受到任何投诉和质疑,已经被告上法庭的某APP决定也使用我们的用户条款。 设计 Android松耦...
摘要:异步任务的构造方法主要用于初始化线程池先关的成员变量创建一个新的异步任务。所以,我们是必须确保在销毁活动之前取消任务。 目录介绍 01.先看下AsyncTask用法 02.AsyncTask源码深入分析 2.1 构造方法源码分析 2.2 看execute(Params... params)方法 2.3 mWorker和mFuture的创建过程 03.异步机制的实现 04.不同...
摘要:上次讲到是如何解析大整数的,一笔带过了的处理,再详细阅读该函数的源码,以下是小分析。总结阅读完这个函数的源码,学习到的是浮动数与字符串的互相转换的实现细节,字符串与浮点数之间的关系较复杂,之后还要继续学习。 上次讲到PHP是如何解析大整数的,一笔带过了number_format的处理,再详细阅读该函数的源码,以下是小分析。 函数原型 string number_format ( flo...
摘要:在我们深入研究这项新鲜的技术之前,让我们先快速的复习原理的相关知识。同时,希望本文能对大家有所帮助。工欲善其事,必先利其器。 flex.css快速入门,极速布局 什么是flex.css? css3 flex 布局相信很多人已经听说过甚至已经在开发中使用过它,但是我想我们都会有一个共同的经历,面对它的各种版本,各种坑,傻傻的分不清楚,flex.css就是对flex布局的一种封装,通过简洁...
阅读 2823·2021-11-11 16:55
阅读 2967·2021-10-18 13:34
阅读 389·2021-10-14 09:42
阅读 3125·2021-10-12 10:12
阅读 1427·2021-09-03 10:30
阅读 690·2021-08-05 10:02
阅读 789·2019-08-30 11:27
阅读 3331·2019-08-29 15:14