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);
}
}
}
上面的代码片段来自实现SerialExcutor的AsyncTask源代码,但我不明白它是如何工作的。
当一个新任务到达时,它被放入一个ArrayDeque的末尾,只有当前没有正在执行的其他任务时,才会执行ArrayDeque顶部的任务。 (当mActive == null时)。
因此,如果在新任务到达时正在执行某个任务,那么当任务完成执行时,没有任何东西会被触发,ArrayDeque如何知道弹出顶部的下一个任务来执行它?
答案 0 :(得分:5)
任务在THREAD_POOL_EXECUTOR
的单独线程上执行,该线程接收Runnable
。在此Runnable
中,当运行任务因某种原因完成时,scheduleNext()
块中会调用finally
。如果队列中有任何任务,则将执行第一个任务,否则执行器将处于空闲状态,直到下一次调用execute()
为止。此外,synchronized
可确保execute()
和scheduleNext()
无法同时在单独的线程中运行。
答案 1 :(得分:0)
让我们深入研究SerialExecutor类。在本课程中我们有最终的
ArrayDeque<Runnable> mTasks = new ArrayDeque<Runnable>();
这实际上可以作为不同线程上不同请求的序列化程序。这是半同步半异步模式的一个例子。
现在让我们来看看串行执行器是如何做到这一点的。请查看SerialExecutor代码中写为
的部分if (mActive == null) {
scheduleNext();
}
因此,当首次在Asynctask上调用execute时,此代码在主线程上执行(因为mActive将初始化为NULL),因此它将把我们带到scheduleNext()函数。 ScheduleNext()函数编写如下:
protected synchronized void scheduleNext() {
if ((mActive = mTasks.poll()) != null) {
THREAD_POOL_EXECUTOR.execute(mActive);
}
}
因此在schedulenext()函数中,我们使用Runnable对象初始化mActive,我们已经在出列结束时插入了该对象。然后,在从线程池中获取的线程上执行此Runnable对象(它只是mActive)。在该线程中,然后&#34;最后&#34;块被执行。
现在有两种情况。
已创建另一个AsyncTask实例,并在执行第一个任务时调用其上的execute方法。
第一个任务执行时,在同一个AsyncTask实例上第二次调用Execute方法。
场景I :如果我们查看execute
的{{1}}函数,我们会发现我们实际上创建了一个新的可运行线程(Say thread t)进行处理后台任务。
在该线程t中,我们运行SerialExecutor
的run函数。但是因为它在try块中,所以只有在后台任务在该线程中完成后才会执行finally。 (请记住,尝试并最终发生在t)的上下文中。
在finally块中,当我们调用scheduleNext函数时,mActive
变为NULL,因为我们已经清空了队列。但是,如果创建了相同mActive
的另一个实例并且我们对它们调用execute,则由于执行前的synchronization关键字而不会执行这些AsyncTask
的执行函数,并且因为{{ 1}}是一个静态实例(因此同一个类的所有对象将共享同一个实例......它是类级别锁定的一个例子)我的意思是没有相同的AsyncTask类的实例可以抢占执行函数(因此在线程t)中运行的后台任务。这一切意味着只有一个活动线程运行任务。对于不同的任务,此线程可能不同,但一次只有一个线程将执行该任务。因此,后面的任务将仅在第一个任务完成时一个接一个地执行,这就是为什么它被称为AsyncTask
。
场景II:在这种情况下,我们会收到异常错误。要理解为什么不能在同一个Asynctask对象上多次调用execute函数,请查看以下SERIAL_EXECUTOR
SerialExecutor
中的代码片段,特别是在下面提到的部分:
AsyncTask.java
从上面的代码片段可以清楚地看到,如果我们在任务处于运行状态时调用execute function两次,它会抛出IllegalStateException,说“无法执行任务:任务已在运行。”。
您可以阅读我关于AsyncTask内部的讨论