AsyncTask SerialExecutor如何工作?

时间:2013-10-06 10:29:29

标签: android android-asynctask

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如何知道弹出顶部的下一个任务来执行它?

2 个答案:

答案 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;块被执行。

现在有两种情况。

  1. 已创建另一个AsyncTask实例,并在执行第一个任务时调用其上的execute方法。

  2. 第一个任务执行时,在同一个AsyncTask实例上第二次调用Execute方法。

  3. 场景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内部的讨论

      

    executeOnExecutor