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<Params, Progress, Result> execute(Params... params) {
        return executeOnExecutor(sDefaultExecutor, params);
    }

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

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

 @MainThread
    public final AsyncTask<Params, Progress, Result> 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<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();
            }
        }
        //任务队列中如果有新任务,则将该任务从队列中取出,交给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<Runnable> sPoolWorkQueue =
            new LinkedBlockingQueue<Runnable>(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<Params, Result>() {
            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<Result>(this, result));
        message.sendToTarget();
        return result;
    }

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

    //创建10个串行AysnTask ,延迟500ms输出    
   for(int i =0;i<10;i++) {
            new AsyncTask<Void, Void, Void>() {
                @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<Void, Void, Void>() {
                @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提交的任务完成即可准备执行,不需要等待并行提交的任务完成;

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
异步任务(AsyncTask)和线程池都是将网络操作放到子线程中执行的常用方式。 1. AsyncTask方式 使用AsyncTask时需要重写其doInBackground()方法,在其中执行耗时操作,例如网络请求。然后可以在onPostExecute()方法中更新UI界面。 下面是一个使用AsyncTask进行网络请求的例子: ``` public class MyAsyncTask extends AsyncTask<String, Void, String> { @Override protected String doInBackground(String... params) { String url = params[0]; String result = ""; try { URL urlObj = new URL(url); HttpURLConnection conn = (HttpURLConnection) urlObj.openConnection(); conn.setRequestMethod("GET"); conn.setConnectTimeout(5000); conn.setReadTimeout(5000); if (conn.getResponseCode() == 200) { InputStream is = conn.getInputStream(); ByteArrayOutputStream bos = new ByteArrayOutputStream(); byte[] buffer = new byte[1024]; int len; while ((len = is.read(buffer)) != -1) { bos.write(buffer, 0, len); } result = new String(bos.toByteArray()); bos.close(); is.close(); } conn.disconnect(); } catch (Exception e) { e.printStackTrace(); } return result; } @Override protected void onPostExecute(String result) { // 在这里更新UI界面 super.onPostExecute(result); } } ``` 2. 线程池方式 线程池是一个管理线程的容器,可以创建多个线程,将任务分配给这些线程执行。这样可以避免频繁地创建和销毁线程,提高了线程的利用率。 下面是一个使用线程池进行网络请求的例子: ``` public class MyThreadPool { private static ExecutorService executorService = Executors.newFixedThreadPool(5); public static void execute(Runnable runnable) { executorService.execute(runnable); } } public class MyRunnable implements Runnable { private String url; public MyRunnable(String url) { this.url = url; } @Override public void run() { String result = ""; try { URL urlObj = new URL(url); HttpURLConnection conn = (HttpURLConnection) urlObj.openConnection(); conn.setRequestMethod("GET"); conn.setConnectTimeout(5000); conn.setReadTimeout(5000); if (conn.getResponseCode() == 200) { InputStream is = conn.getInputStream(); ByteArrayOutputStream bos = new ByteArrayOutputStream(); byte[] buffer = new byte[1024]; int len; while ((len = is.read(buffer)) != -1) { bos.write(buffer, 0, len); } result = new String(bos.toByteArray()); bos.close(); is.close(); } conn.disconnect(); } catch (Exception e) { e.printStackTrace(); } // 在这里更新UI界面 } } ``` 使用线程池时,需要先创建一个ExecutorService对象,然后通过execute()方法提交任务。在任务的run()方法中执行网络请求,并在其中更新UI界面。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值