About ExecutorService(1),Future&FutureTask
About ExecutorService(2),自定义线程池
About ExecutorService(3),我所认识的AsyncTask
About ExecutorService(4),AsyncTask番外篇
打开电脑的时候已经深夜十二点多了,周末两天过的实在憋屈,小伙伴喊我去打球,因为脚趾的伤至少还要数周才能痊愈,于是当了一天的啦啦队,第二天果断没再去。。。
这一篇主要跟大家聊一聊,我所认识的AT。
提到AT,童鞋们一定首先想到的异步,其次,还可能引申出其他名词,比如,串行,并发,并行。很多人认为并发和并行是一码事,下面我就说一说,我对这个四种模式的理解。
异步:异步和同步是是相对的,同步就是执行完一个再执行另一个,需要等待。异步就是彼此相对独立,再等待某件事的过程中继续做自己的事,不需要等待这一件事完成后再去做,举一个经典的不能在经典的例子,我的舍友花花同学的大金表坏了,让我去修,我到了商场,把手表递给修表师傅,修表这个时间段我可以逛商场,只需修完通知我就行了,我们两个就是彼此相互独立的。小结一下,可以说,异步是一个目的,更强调结果。而多线程是实现异步的一种方式。主线程不需要同步等待子线程的完成,从而可以干其他事。实现异步可以采用多线程技术,也可以交给另外的进程来处理。
并发:是指一个时间段中有几个程序都处于已启动运行到运行完毕之间,且这几个程序都是在同一个处理机上运行。其中两种并发关系分别是同步和互斥。举个例子说明一下,我的舍友花花比我起床早,去厕所洗漱,因为厕所只能容纳一个人,我起床后眼看要迟到了,于是就把刷一半牙的花花揪了出来,那么,我俩都满足由起床到洗漱完毕之间,并且是属于互斥关系的并发。
串行:跟同步相似,意思就是在一个时间片上只允许一个任务在进行,多个任务的执行,后一个任务必须等待前一个任务执行完毕才能工作,跟单核CPU一个时间片内只能执行一个任务类似。举个例子,今天我休息,花花洗漱完,我再去洗漱。
并行:在单CPU中多道程序设计系统中,进程被交替执行,表现出一种并发的外部特种;在多处理器系统中,进程不仅可以交替执行,而且可以重叠执行。在多核CPU上的程序才可实现并行处理。从而可知,并行是针对多处理器而言的。并行是同时发生的多个并发事件,具有并发的含义,但并发不一定并行,也亦是说并发事件之间不一定要同一时刻发生。举个例子,搬家了,厕所能同时容纳我和花花两个人了。
在Android包下android\app\ActivityThread.java类中,有这样一段代码
// If the app is Honeycomb MR1 or earlier, switch its AsyncTask
// implementation to use the pool executor. Normally, we use the
// serialized executor as the default. This has to happen in the
// main thread so the main looper is set right.
if (data.appInfo.targetSdkVersion <= android.os.Build.VERSION_CODES.HONEYCOMB_MR1) {
AsyncTask.setDefaultExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
}
AsyncTask中hide函数
/**
* @hide
*/
public static void setDefaultExecutor(Executor exec) {
sDefaultExecutor = exec;
}
通过查询,得知在Android 3.1以及之前的版本使用的是THREAD_POOL_EXECUTOR
,之后使用的是SERIAL_EXECUTOR
。
/**
* May 2011: Android 3.1.
*/
public static final int HONEYCOMB_MR1 = 12;
THREAD_POOL_EXECUTOR
是一个自定义的线程池。多核时代的线程池意味着并行和异步,(其实这里说异步,并不确切,因为异步强调是多个工作线程完成同一个任务,侧重专业性和目的性)是一种高效的任务执行模式。
这个SERIAL_EXECUTOR
同样也是以EXECUTOR结尾的,是不是另一种线程池呢?
/**
* An {@link Executor} that executes tasks one at a time in serial
* order. This serialization is global to a particular process.
*/
public static final Executor SERIAL_EXECUTOR = new SerialExecutor();
private static class SerialExecutor implements Executor {
final ArrayDeque<Runnable> mTasks = new ArrayDeque<Runnable>();
Runnable mActive;
public synchronized void execute(final Runnable r) {
mTasks.offer(new Runnable() {
public void run() {
try {
r.run();
} finally {
scheduleNext();
}
}
});
if (mActive == null) {
scheduleNext();
}
}
protected synchronized void scheduleNext() {
if ((mActive = mTasks.poll()) != null) {
THREAD_POOL_EXECUTOR.execute(mActive);
}
}
}
Executor 接口如下:
public interface Executor {
/**
* Executes the given command at some time in the future. The command
* may execute in a new thread, in a pooled thread, or in the calling
* thread, at the discretion of the {@code Executor} implementation.
*
* @param command the runnable task
* @throws RejectedExecutionException if this task cannot be
* accepted for execution
* @throws NullPointerException if command is null
*/
void execute(Runnable command);
}
看完这段代码,就恍然大悟,差一点就被AT的命名给坑了,原来SERIAL_EXECUTOR
只是一个实现了Executor接口的内部静态类,内部真正执行任务的依然是THREAD_POOL_EXECUTOR
线程池,它存在的作用就是,把任务队列化,前一个任务执行完毕后,后一个任务才会被提交到线程池中,那么线程池中始终是只有一个工作线程在执行任务,也只有一个任务,这样的AT就变成了串行工作模式。虽然效率降低了,但是提高了稳定性,避免了因任务过多,导致线程池缓冲队列膨胀带来的FC隐患。
总结:AT中doInBackground
,onPostExecute
等方法,只是一些回调接口,真正实现异步访问的是FutureTask,包括AT的取消,也是通过中断FutureTask中的任务。FutureTask任务的执行又需要线程池来做支撑,所以AT又在外面包了一层线程池的实现,只不过这个线程池比较奇葩,通过一个串行工作模式,逐一将任务丢进池内。
说了这么多,为了提高执行效率,我们得发挥起来才能对的起各大厂商的真八核。接下来为大家介绍一个高效的并行模式,Master-Worker。
Master-Worker工作示意图Master-Worker,的核心思想就是又两类进程协调工作:Master线程和Worker线程。Master线程负责接收和分配任务,Worker线程负责处理子任务。当各个Worker进行将子任务处理完成后,将处理结果返回给Master线程,由Master进程做归纳和汇总,从而得到系统的最终结果。
Master-Worker模式好处就是,它能够将一个大任务,并行执行,从而提高系统的执行效率。Client作为请求者,提交任务,Master线程会分配任务并立即返回,并不会等待全部子任务执行完毕后再返回。根据上面对异步的解释,Master-Worker模式的目的性很强,Master和Worker线程是协调完成的,因此Master-Worker模式是典型的利用多线程处理数据的异步工作模式。
我的舍友花花,最近在温习基础知识,刚刚看到九九乘法表,瞥了一眼,发现他的笔记上还再用双层for循环嵌套的方式进行计算,这种方法只适合打印出美观的阶梯式乘法表,但是如果放到项目中,这种算法太慢,效率太低。如果涉及平方和相加就更慢了,更甭提立方和了。
于是,我通过Master-Worker模式,写了一个demo,供大家参考。
依然提前写完了,再次附上链接。
至此,我所认识的ExecutorService介绍完毕,可能有些遗忘的地方,有些地方的讲解可能不到位或者有错误,希望朋友们多提意见,本人会持续更新博客。
片尾Tip:3月15日,ios 8.3Beta。
网友评论