多個AsyncTask 相互阻塞的緣由

構造AsyncTask, 這裏只需理解Worker, Future的概念。注意這裏mWorkermFuture 的關係, 以及 postResult 和done方法。java

AsyncTask使用了模板方法的設計模式, doInBackground是其中的核心,被包裝在mWorker對象中,mWorker對象又被包裝在mFuture對象, mFuture本質是一個runnable
android

 /**
     * Creates a new asynchronous task. This constructor must be invoked on the UI thread.
     */
    public AsyncTask() {
        mWorker = new WorkerRunnable<Params, Result>() {
            public Result call() throws Exception {
                mTaskInvoked.set(true);

                Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
                //noinspection unchecked
                return postResult(doInBackground(mParams));
            }
        };

        mFuture = new FutureTask<Result>(mWorker) {
            @Override
            protected void done() {
                try {
                    postResultIfNotInvoked(get());
                } catch (InterruptedException e) {
                    android.util.Log.w(LOG_TAG, e);
                } catch (ExecutionException e) {
                    throw new RuntimeException("An error occured while executing doInBackground()",
                            e.getCause());
                } catch (CancellationException e) {
                    postResultIfNotInvoked(null);
                }
            }
        };
    }


===============以上是背景。==================設計模式


從execute方法開始,內部調用了executeOnExecutor 方法, 多線程

 public final AsyncTask<Params, Progress, Result> execute(Params... params) {
        return executeOnExecutor(sDefaultExecutor, params);
    }

這裏的sDefaultExecutor 默認是SERIAL_EXECUTOR,是 SerialExecutor類型對象。 模板方法doInBackground的參數params就是在這裏經過mWorker對象賦值的async

public final AsyncTask<Params, Progress, Result> executeOnExecutor(Executor exec,
            Params... params) {
       
       ...
       
       mStatus = Status.RUNNING;

        onPreExecute();

        mWorker.mParams = params;
        exec.execute(mFuture);

        return this;
    }


AsyncTask中有兩個重要的靜態變量:SERIAL_EXECUTOR 和 THREAD_POOL_EXECUTORide

/**
     * An {@link Executor} that can be used to execute tasks in parallel.
     */
    public static final Executor THREAD_POOL_EXECUTOR
            = new ThreadPoolExecutor(CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE,
                    TimeUnit.SECONDS, sPoolWorkQueue, sThreadFactory);

    /**
     * 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();


 接下來看 SerialExecutor 的execute 方法, 先把當前task放在隊列mTasks尾部,而後取隊列第一個task經過TREAD_POOL_EXECUTOR執行。 注意executescheduleNext 都是synchronized 方法, 在AysncTask中SerialExecutor的實例變量SERIAL_EXECUTOR又是一個靜態成員,因此即便咱們在不一樣的線程中new出來不少AyncTask對象分別提交不一樣的任務, 這些任務讓仍然會被串行的加入任務隊列。工具

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();
                    }
                }
            });
            //若是當前沒有正在執行的任務就在隊首調度一個新任務,
            //不然等待當前任務完成,完成後會自動調度下一個任務, 這裏的mActive是包裝後的任務對象
            if (mActive == null) {
                scheduleNext();
            }
        }

        protected synchronized void scheduleNext() {
            if ((mActive = mTasks.poll()) != null) {
            // SERIAL_EXECUTOR調用了THREAD_POOL_EXECUTOR來具體執行任務
            //注意這裏的mActive是包裝後的任務對象
                THREAD_POOL_EXECUTOR.execute(mActive);
            }
        }
    }

千萬注意,這裏咱們提交的任務是 r , 可是在放入mTasks隊列時對 r 進行了以下的包裝, 這裏包裝後的任務對象是匿名的,假設包裝後的任務叫pr ( pseudo r 的意思) , 那麼pr 的執行包括了兩部分,首先執行具體的任務(r的run方法),而後無論任務執行是否成功(也就是無論r的run方法過程是否發生了異常),接着調度mTasks隊列的下一個任務。因此當使用默認的AsyncTask時,若是有多處提交任務,那麼這些任務將會按串行的方式執行,這就是爲何官方推薦只用AsyncTask執行短時任務, 任務耗時太長的話將會發生相互阻塞,這多是你不想要的!post

mTasks.offer(new Runnable() {
                public void run() {
                    try {
                        r.run();
                    } finally {
                        scheduleNext();
                    }
                }
            });


問題到這裏已經清楚了。this

=================end======================spa

接着說一下, TREAD_POOL_EXECUTOR 和 SERIAL_EXECUTOR均可以當作executor使用, 而且TREAD_POOL_EXECUTOR是能夠並行的執行任務的(多線程),可是TREAD_POOL_EXECUTOR對AsyncTask是不可見的,SERIAL_EXECUTOR纔是AsyncTask直接使用的executor對象。 TREAD_POOL_EXECUTOR只是做爲SERIAL_EXECUTOR執行任務的工具,對SERIAL_EXECUTOR來講,它的做用就是能夠提供線程來執行任務。因此若是你想改變AsyncTask不適合執行長時間任務的這個缺陷,須要定製本身的Executor來替換SERIAL_EXECUTOR。

相關文章
相關標籤/搜索