分析AsyncTask中的线程池

问题由来

之前看到一篇博文,说AsyncTask不适合运行多任务, 多个任务不会异步执行, 当时只是印象里记住了一下也不确定, 今天把代码看了看, 把原因写出来。

问题的代码演示

 1 public class AsyncTaskDemo  extends AsyncTask<String, Integer, String>{
 2     private final static String TAG = "AsyncTaskTest";
 3
 4     @Override
 5     protected String doInBackground(String... params) {
 6         Log.v(TAG, params[0] + "=================doInBackground===================PID = " + Thread.currentThread().getId());
 7
 8         return params[0];
 9     }
10
11
12 }
1 // 一个按钮的onclick函数
2 public void test(View view) {
3         Log.v("AsyncTaskTest", "CPU_COUNT = " + Runtime.getRuntime().availableProcessors());
4
5         new AsyncTaskDemo().execute("Hello1");
6         new AsyncTaskDemo().execute("Hello2");
7         new AsyncTaskDemo().execute("Hello3");
8         new AsyncTaskDemo().execute("Hello4");
9     }
//logcat里打印出来的结果

12-11 16:07:34.865    1979-1979/com.sabo.helloworld V/AsyncTaskTest﹕ CPU_COUNT = 1
12-11 16:07:34.871    1979-2102/com.sabo.helloworld V/AsyncTaskTest﹕ Hello1=================doInBackground===================PID = 161
12-11 16:07:34.872    1979-2103/com.sabo.helloworld V/AsyncTaskTest﹕ Hello2=================doInBackground===================PID = 162
12-11 16:07:34.872    1979-2103/com.sabo.helloworld V/AsyncTaskTest﹕ Hello3=================doInBackground===================PID = 162
12-11 16:07:34.872    1979-2103/com.sabo.helloworld V/AsyncTaskTest﹕ Hello4=================doInBackground===================PID = 162

实验大致辅证了"AsyncTask不适合运行多任务"这个推断, 另一个有趣的结果是非异步执行的时候居然可能不是在一个线程里运行的【看我上面用红色标记的部分】。

分析:

1     @MainThread
2     public final AsyncTask<Params, Progress, Result> execute(Params... params) {
3         return executeOnExecutor(sDefaultExecutor, params);
4     }
    @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);// exec  --> sDefaultExecutor

        return this;
    }

如果熟悉Java的并发编程的话就知道sDefaultExecutor用于将要完成的任务交给内部已经实现的线程池去执行(有兴趣的话可以去看看Doug Lea写的那本《Java并发编程》)

public static final Executor SERIAL_EXECUTOR = new SerialExecutor();

private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;

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);
            }
        }
    }

这里的代码就能解释为什么多个AsyncTask一起执行时序列化执行而被异步的了,

1         new AsyncTaskDemo().execute("Hello1"); //mActive == null
2         new AsyncTaskDemo().execute("Hello2"); //mActive != null
3         new AsyncTaskDemo().execute("Hello3"); //mActive != null
4         new AsyncTaskDemo().execute("Hello4"); //mActive != null

上面红色标记的代码可以看出,只有第一个任务直接调用scheduleNext()--->THREAD_POOL_EXECUTOR.execute(mActive), 线程池直接将任务交由线程去执行,而后面几个任务先被放入ArrayDeque<Runnable> mTasks, 没有交给任何线程去执行,

每个任务执行完之后又都要运行上面绿颜色标记的scheduleNext(), 从而依次序序列化执行任务。

上面提到的有趣的事情,既然AsyncTask是序列化执行任务的, 那么线程池里只要一个线程就能满足要求了啊, 为什么会有两个线程。

1     private static final int CORE_POOL_SIZE = CPU_COUNT + 1;
2     private static final int MAXIMUM_POOL_SIZE = CPU_COUNT * 2 + 1;
3     private static final int KEEP_ALIVE = 1;
4
5     public static final Executor THREAD_POOL_EXECUTOR
6             = new ThreadPoolExecutor(CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE,
7                     TimeUnit.SECONDS, sPoolWorkQueue, sThreadFactory);

上面我把CPU_COUNT的值打印出来是1, 所以这里线程池的线程数就可能是[2, 3]了, 然而对于AsyncTask来说1就够了, 多余1的线程其实是没有什么帮助的。

P.S.

:-)第一次写博客, 加上功力又有限, 欢迎大家指正canbin.zhang#qq.com

时间: 2024-10-13 12:21:39

分析AsyncTask中的线程池的相关文章

从源代码分析Universal-Image-Loader中的线程池

一般来讲一个网络访问就需要App创建一个线程来执行,但是这也导致了当网络访问比较多的情况下,线程的数目可能积聚增多,虽然Android系统理论上说可以创建无数个线程,但是某一时间段,线程数的急剧增加可能导致系统OOM.在UIL中引入了线程池这种技术来管理线程.合理利用线程池能够带来三个好处.第一:降低资源消耗.通过重复利用已创建的线程降低线程创建和销毁造成的消耗.第二:提高响应速度.当任务到达时,任务可以不需要等到线程创建就能立即执行.第三:提高线程的可管理性.线程是稀缺资源,如果无限制的创建,

android AsyncTask 只能在线程池里单个运行的问题

android 的AysncTask直接调用Execute会在在一个线程池里按调用的先后顺序依次执行. 如果应用的所有网络获取都依赖这个来做,当有一个网络请求柱塞,就导致其它请求也柱塞了. 在3.0 以后引入了新的方法.可以不在一个线程池里运行. class TaskHelper { public static <P, T extends AsyncTask<P, ?, ?>> void execute(T task) { execute(task, (P[]) null); }

Java中的线程池

综述 在我们的开发中经常会使用到多线程.例如在Android中,由于主线程的诸多限制,像网络请求等一些耗时的操作我们必须在子线程中运行.我们往往会通过new Thread来开启一个子线程,待子线程操作完成以后通过Handler切换到主线程中运行.这么以来我们无法管理我们所创建的子线程,并且无限制的创建子线程,它们相互之间竞争,很有可能由于占用过多资源而导致死机或者OOM.所以在Java中为我们提供了线程池来管理我们所创建的线程. 线程池的使用 采用线程池的好处 在这里我们首先来说一下采用线程池的

《Java并发编程的艺术》 第9章 Java中的线程池

第9章 Java中的线程池 在开发过程中,合理地使用线程池能带来3个好处: 降低资源消耗.通过重复利用已创建的线程 降低线程创建和销毁造成的消耗. 提高响应速度.当任务到达时,任务可以不需要等到线程创建就能立即执行. 提高线程的可管理性.线程是稀缺资源,如果无限制地创建,不仅会消耗系统资源,还会降低系统的稳定性,使用线程池可以进行统一分配.调优和监控. 9.1 线程池的实现原理 当提交一个新任务到线程池时,线程池的处理流程如下: 1)线程池判断核心线程池里的线程是否都在执行任务.如果不是,则创建

Java中的线程池ExecutorService

示例 import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; public class Ch09_Executor { private static void run(ExecutorService threadPool) { for(int i = 1; i < 5; i++)

Java中的线程池——ThreadPoolExecutor的原理

1 线程池的处理流程向线程池提交一个任务后,它的主要处理流程如下图所示一个线程从被提交(submit)到执行共经历以下流程: 线程池判断核心线程池里是的线程是否都在执行任务,如果不是,则创建一个新的工作线程来执行任务.如果核心线程池里的线程都在执行任务,则进入下一个流程线程池判断工作队列是否已满.如果工作队列没有满,则将新提交的任务储存在这个工作队列里.如果工作队列满了,则进入下一个流程.线程池判断其内部线程是否都处于工作状态.如果没有,则创建一个新的工作线程来执行任务.如果已满了,则交给饱和策

Linux中epoll+线程池实现高并发

服务器并发模型通常可分为单线程和多线程模型,这里的线程通常是指"I/O线程",即负责I/O操作,协调分配任务的"管理线程",而实际的请求和任务通常交由所谓"工作者线程"处理.通常多线程模型下,每个线程既是I/O线程又是工作者线程.所以这里讨论的是,单I/O线程+多工作者线程的模型,这也是最常用的一种服务器并发模型.我所在的项目中的server代码中,这种模型随处可见.它还有个名字,叫"半同步/半异步"模型,同时,这种模型也是生

Spring中的线程池ThreadPoolTaskExecutor

1.直接调用Spring框架中的ThreadPoolTaskExecutor ThreadPoolTaskExecutor poolTaskExecutor = new ThreadPoolTaskExecutor(); //线程池所使用的缓冲队列 poolTaskExecutor.setQueueCapacity(200); //线程池维护线程的最少数量 poolTaskExecutor.setCorePoolSize(5); //线程池维护线程的最大数量 poolTaskExecutor.s

Tomcat中的线程池StandardThreadExecutor

之所以今天讨论它,因为在motan的的NettyServer中利用它这个线程池可以作为业务线程池,它定制了一个自己的线程池.当然还是基于jdk中的ThreadExecutor中的构造方法和execute方法,然后在外边包装一层. public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue,