AsyncTask 的两个线程池原理探究

熟悉AsyncTask的人都知道,AsyncTask中维护了两个线程池

    //第一个线程池,用作执行具体任务
    public static final Executor THREAD_POOL_EXECUTOR;

    static {
        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
                CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE_SECONDS, TimeUnit.SECONDS,
                sPoolWorkQueue, sThreadFactory);
        threadPoolExecutor.allowCoreThreadTimeOut(true);
        THREAD_POOL_EXECUTOR = threadPoolExecutor;
    }

    /**
     * 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 final int MESSAGE_POST_RESULT = 0x1;
    private static final int MESSAGE_POST_PROGRESS = 0x2;
    //注意sDefaultExecutor ,马上就会提到
    private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;

其中第二个线程池SERIAL_EXECUTOR 只用作排队,具体任务的执行仍由THREAD_POOL_EXECUTOR进行处理;

让我们从AsyncTask的execute函数查看执行原理;

    @MainThread
    public final AsyncTask execute(Params... params) {
        return executeOnExecutor(sDefaultExecutor, params);
    }

可以看到,execute函数默认调用了executeOnExecutor 方法,默认执行线程是sDefaultExecutor,即默认使用SERIAL_EXECUTOR来执行任务;

那么这个SERIAL_EXECUTOR到底做了什么呢

 @MainThread
    public final AsyncTask executeOnExecutor(Executor exec,
            Params... params) {
        if (mStatus != Status.PENDING) {
            switch (mStatus) {
                case RUNNING:
                    throw new IllegalStateException("Cannot execute task:"
                            + " the task is already running.");
                case FINISHED:
                    throw new IllegalStateException("Cannot execute task:"
                            + " the task has already been executed "
                            + "(a task can be executed only once)");
            }
        }

        mStatus = Status.RUNNING;

        onPreExecute();

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

        return this;
    }

可以看到这个executeOnExecutor函数,首先确保该AsyncTask只执行一次,然后执行onPreExecute,真正的执行逻辑是调用了SERIAL_THREADPOOL的execute方法;

    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();
                    }
                }
            });
           //当前没有任务在被执行,则尝试加入新任务
            if (mActive == null) {
                scheduleNext();
            }
        }
        //任务队列中如果有新任务,则将该任务从队列中取出,交给THREAD_POOL线程池执行;
        protected synchronized void scheduleNext() {
            if ((mActive = mTasks.poll()) != null) {
                THREAD_POOL_EXECUTOR.execute(mActive);
            }
        }
    }

再找到这个类的execute方法。可以非常清楚的知道,这个线程池仅仅用作排队使用,并且保证了THREAD_POOL一次只执行一个任务;

单看这一步,其实可以认为 两个线程池是没有必要的,一个执行,一个排队,就是标准的newSingleThreadExecutor ,那么为什么设计时候要使用两个线程池提高资源浪费呢;

答案就在马上揭晓:

我们正常使用asyncTask,往往是AysnTask.execute();内部帮我们调用了executeOnExecutor(sDefaultExecutor, params);

但是Google同样给我们留下了并行的方法,我们可以自己调用executeOnExecutor(THREAD_POOL);来并行执行任务;而且这种策略有一个好处;默认的队列执行是线性的,但是通过源码看出,线性的意思是,只有SERIAL_EXECUTOR提交的任务需要线性执行,我们通过THREAD_POOL执行的任务是不受影响的,即使正在运行,只要mActive为空,那么SERIAL_EXECUTOR将会提交新任务给THREAD_POOL执行;因此,这种设计,能够让线性的保持线性,不需要线性的任务就直接插队进行并行处理;

下面展示THREAD_POOL的构造源码,一次最多只能并发执行4个任务;

    // We want at least 2 threads and at most 4 threads in the core pool,
    // preferring to have 1 less than the CPU count to avoid saturating
    // the CPU with background work
    private static final int CORE_POOL_SIZE = Math.max(2, Math.min(CPU_COUNT - 1, 4));
    private static final int MAXIMUM_POOL_SIZE = CPU_COUNT * 2 + 1;
    private static final int KEEP_ALIVE_SECONDS = 30;

    private static final ThreadFactory sThreadFactory = new ThreadFactory() {
        private final AtomicInteger mCount = new AtomicInteger(1);

        public Thread newThread(Runnable r) {
            return new Thread(r, "AsyncTask #" + mCount.getAndIncrement());
        }
    };

    private static final BlockingQueue sPoolWorkQueue =
            new LinkedBlockingQueue(128);

    /**
     * An {@link Executor} that can be used to execute tasks in parallel.
     */
    public static final Executor THREAD_POOL_EXECUTOR;

    static {
        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
                CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE_SECONDS, TimeUnit.SECONDS,
                sPoolWorkQueue, sThreadFactory);
        threadPoolExecutor.allowCoreThreadTimeOut(true);
        THREAD_POOL_EXECUTOR = threadPoolExecutor;
    }

 

回到开始,我们能看到,AsyncTask的任务提交函数都通过注解设定了MainThread,这是为什么呢?

首先,我们回到使用AsyncTask的使用上,首先,他先在主线程执行onPreExecute(),可以做一些加载图标,对话框,进度条之类的。再在子线程执行doInBackground(),最后,我们希望在执行结束后,在主线程执行onPostExecute(),取消对话框 进度条,或者其他的View的更新。

因此,我们需要在切换子线程和主线程;

    private static Handler getMainHandler() {
        synchronized (AsyncTask.class) {
            if (sHandler == null) {
                sHandler = new InternalHandler(Looper.getMainLooper());
            }
            return sHandler;
        }
    }

    private Handler getHandler() {
        return mHandler;
    }
    .........
    .........
    .........
    public AsyncTask(@Nullable Looper callbackLooper) {
        mHandler = callbackLooper == null || callbackLooper == Looper.getMainLooper()
            ? getMainHandler()
            : new Handler(callbackLooper);

        mWorker = new WorkerRunnable() {
            public Result call() throws Exception {
                mTaskInvoked.set(true);
                Result result = null;
                try {
                    Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
                    //noinspection unchecked
                    result = doInBackground(mParams);
                    Binder.flushPendingCommands();
                } catch (Throwable tr) {
                    mCancelled.set(true);
                    throw tr;
                } finally {
                    postResult(result);
                }
                return result;
            }
        };

我们在主线程创建,保证了handler在主线程创建,便于onProgressUpdate, onPostExecute等 可以正常更新UI。 

    private Result postResult(Result result) {
        @SuppressWarnings("unchecked")
        Message message = getHandler().obtainMessage(MESSAGE_POST_RESULT,
                new AsyncTaskResult(this, result));
        message.sendToTarget();
        return result;
    }

下面给出一个简单的Demo,展示一下并发AsyncTask和串行AsyncTask的使用结果;

    //创建10个串行AysnTask ,延迟500ms输出    
   for(int i =0;i<10;i++) {
            new AsyncTask() {
                @Override
                protected void onPreExecute() {
                    super.onPreExecute();
                }

                @Override
                protected void onPostExecute(Void aVoid) {


                }

                @Override
                protected Void doInBackground(Void... voids) {
                    try {
                        Thread.sleep(500);
                    } catch (InterruptedException e) {

                    }
                    Log.e("AsynTask" +
                            "", "finished");

                    return null;
                }
            }.execute(null, null, null);
        }
  //每隔200ms 创建1个并行AysnTask ,延迟500ms输出    
        for(int i =0;i<10;i++) {
            new AsyncTask() {
                @Override
                protected void onPreExecute() {
                    super.onPreExecute();
                }

                @Override
                protected void onPostExecute(Void aVoid) {
                }

                @Override
                protected Void doInBackground(Void... voids) {
                    try {
                        Thread.sleep(500);
                    } catch (InterruptedException e) { 
                    }
                    Log.e("AsynTask" +
                            "", "finishedCon");
                    return null;
                }
            }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
            try {
                Thread.sleep(200);
            } catch (InterruptedException e) {

            }

忽略指令执行耗时,我们一次性创建了10个串行和1个并行,之后每0.2s创建了1个并行,创建完10个后 花费1.8秒;此时打印了3个串行;

我们看一下打印结果

//创建了第10个串行和一个并行,等待500ms打印第一个串行和第一个并行,然后第二个串行进入等待
08-16 20:38:40.202 26282-26346/com.example.administrator.singletoptest E/AsynTask: finished
08-16 20:38:40.202 26282-26347/com.example.administrator.singletoptest E/AsynTask: finishedCon
//再过200ms,此时第700ms,打印第二个并行
08-16 20:38:40.405 26282-26348/com.example.administrator.singletoptest E/AsynTask: finishedCon
//900ms,第三个并行
08-16 20:38:40.607 26282-26349/com.example.administrator.singletoptest E/AsynTask: finishedCon
//1000ms, 第二个并行等待500ms后,打印
08-16 20:38:40.707 26282-26350/com.example.administrator.singletoptest E/AsynTask: finished
//1100,第三个并行,打印;
08-16 20:38:40.810 26282-26351/com.example.administrator.singletoptest E/AsynTask: finishedCon
//1300
08-16 20:38:41.013 26282-26352/com.example.administrator.singletoptest E/AsynTask: finishedCon
//1500,第三个串行打印完成,第5个并行打印完成,此时第4个串行进入等待;
08-16 20:38:41.211 26282-26353/com.example.administrator.singletoptest E/AsynTask: finished
08-16 20:38:41.213 26282-26354/com.example.administrator.singletoptest E/AsynTask: finishedCon
//1700,不再分析
08-16 20:38:41.415 26282-26346/com.example.administrator.singletoptest E/AsynTask: finishedCon
08-16 20:38:41.616 26282-26347/com.example.administrator.singletoptest E/AsynTask: finishedCon
08-16 20:38:41.712 26282-26353/com.example.administrator.singletoptest E/AsynTask: finished
08-16 20:38:41.816 26282-26349/com.example.administrator.singletoptest E/AsynTask: finishedCon
08-16 20:38:42.019 26282-26350/com.example.administrator.singletoptest E/AsynTask: finishedCon
08-16 20:38:42.213 26282-26353/com.example.administrator.singletoptest E/AsynTask: finished
08-16 20:38:42.714 26282-26353/com.example.administrator.singletoptest E/AsynTask: finished
08-16 20:38:43.216 26282-26353/com.example.administrator.singletoptest E/AsynTask: finished
08-16 20:38:43.718 26282-26353/com.example.administrator.singletoptest E/AsynTask: finished
08-16 20:38:44.220 26282-26353/com.example.administrator.singletoptest E/AsynTask: finished
08-16 20:38:44.721 26282-26353/com.example.administrator.singletoptest E/AsynTask: finished

总结:

1、AsyncTask,默认的两个线程池可以用一个newSingleThreadExecutor 单任务线程池代替

2、使用两个线程池,可以做到需要串行的任务保持串行提交任务,不需要串行完成的任务可以插队进行并行提交;

3、串行的任务只需要SERIAL_EXECUTOR提交的任务完成即可准备执行,不需要等待并行提交的任务完成;

你可能感兴趣的:(Android)