多个AsyncTask 相互阻塞的原因

2019独角兽企业重金招聘Python工程师标准>>> hot3.png

构造AsyncTask, 这里只需理解Worker, Future的概念。注意这里mWorkermFuture 的关系, 以及 postResult 和done方法。

AsyncTask使用了模板方法的设计模式, doInBackground是其中的核心,被包装在mWorker对象中,mWorker对象又被包装在mFuture对象, mFuture本质是一个runnable

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

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

        mFuture = new FutureTask(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 execute(Params... params) {
        return executeOnExecutor(sDefaultExecutor, params);
    }

这里的sDefaultExecutor 默认是SERIAL_EXECUTOR,是 SerialExecutor类型对象。 模板方法doInBackground的参数params就是在这里通过mWorker对象赋值的

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

        onPreExecute();

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

        return this;
    }


AsyncTask中有两个重要的静态变量:SERIAL_EXECUTOR 和 THREAD_POOL_EXECUTOR

/**
     * 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 mTasks = new ArrayDeque();
        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执行短时任务, 任务耗时太长的话将会发生相互阻塞,这可能是你不想要的!

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


问题到这里已经清楚了。

=================end======================

接着说一下, 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。

转载于:https://my.oschina.net/u/255456/blog/339034

你可能感兴趣的:(多个AsyncTask 相互阻塞的原因)