AsyncTask必知必会(一)

AsyncTask的必知必会(一)

转载请注明出处:http://blog.csdn.net/qinjunni2014/article/details/43954081

对与AsyncTask的使用大家一定很熟悉了,其实很简单,创建一个类,继承AsyncTask, 安排好它的参数类型,实现doInBackgrounds(必须), onProgressUpdate, onPreExecute, onPostExecute, 然后execute就好了, 系统就会安排一个线程去执行这个任务, 那么问题来了, 我们知道手机上得资源有限, 如果我们同时指定多个任务执行,系统资源必定无法承受,那么Android如何去妥善的安排他们的执行顺序呢?我们先从一个例子开始看起:

public class MainActivity extends Activity implements View.OnClickListener {

    static final int TASK_COUNT = 10;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        findViewById(R.id.launch_button).setOnClickListener(this);
    }



    public void launchAsyncTasks(int number)
    {
        for(int i=0;i< number; ++i)
        {
            new MyAsyncTask().executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, i);
        }
    }

    @Override
    public void onClick(View v) {
        if(v.getId() == R.id.launch_button)
        {
            launchAsyncTasks(TASK_COUNT);
        }
    }

    class MyAsyncTask extends AsyncTask<Integer, Void,Void>
    {

        @Override
        protected Void doInBackground(Integer... params) {

            int number = params[0];

            Log.e("Junli"," AsyncTask "+ number + "will finish in 5seconds");
            try {
                Thread.sleep(5000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            return null;
        }
    }
}

这段代码中,当我们点击按钮时,会指定TASK_COUNT 个任务同时执行,那么他们的先后顺序会怎样呢?我们看log:

 AsyncTask 0will finish in 5seconds
 AsyncTask 1will finish in 5seconds
 AsyncTask 2will finish in 5seconds
 AsyncTask 3will finish in 5seconds
 AsyncTask 4will finish in 5seconds
===after 5 seconds ...
 AsyncTask 5will finish in 5seconds
 AsyncTask 6will finish in 5seconds
 AsyncTask 7will finish in 5seconds
 AsyncTask 8will finish in 5seconds
 AsyncTask 9will finish in 5seconds

大家可以看到我们使用的是AsyncTask.THREAD_POOL_EXECUTOR这个executor对象去执行asynctask, 它首先会安排5个任务去执行,其余的任务陷入等待,待这5个任务执行完毕之后,再从等待队列中取出5个任务去执行。那么等待队列有多长呢? 如果任务数超过了等待队列的长度会怎么样呢?不急,我们先来看看AsyncTask.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);

这个THREAD_POOL_EXECUTOR实际上就是一个java.util.concurrent.ThreadPoolExecutor类的对象,ThreadPoolExecutor的解释如下:

An ExecutorService that executes each submitted task using one of possibly several pooled threads, normally configured using Executors factory methods.

其中最重要的两个参数是:core_pool_size, maximum_pool_size,

A ThreadPoolExecutor will automatically adjust the pool size (see getPoolSize()) according to the bounds set by corePoolSize (see getCorePoolSize()) and maximumPoolSize (see getMaximumPoolSize()). When a new task is submitted in method execute(java.lang.Runnable), and fewer than corePoolSize threads are running, a new thread is created to handle the request, even if other worker threads are idle. If there are more than corePoolSize but less than maximumPoolSize threads running, a new thread will be created only if the queue is full. By setting corePoolSize and maximumPoolSize the same, you create a fixed-size thread pool. By setting maximumPoolSize to an essentially unbounded value such as Integer.MAX_VALUE, you allow the pool to accommodate an arbitrary number of concurrent tasks. Most typically, core and maximum pool sizes are set only upon construction, but they may also be changed dynamically using setCorePoolSize(int) and setMaximumPoolSize(int).

大意就是:

  1. 如果线程池中运行的线程数量小于core_pool_size, 新任务到达时,会立即创建一个线程去执行这个任务
  2. 如果运行线程数量等于core_pool_size,新任务会进入等待队列中等待
  3. 如果等待队列已满,而且运行线程数量小于maximum_pool_size, 会创建线程去执行任务
  4. 如果等待队列满,而且运行线程数量等于maximum_pool_size,则会调用RejectedExecutionHandler去处理。

下一步我们来修改TASK_COUNT的大小来验证上面的规则:

  1. TASK_COUNT = 15;
  2. TASK_COUNT = 137;
  3. TASK_COUNT = 150;

来看看在三中情况下, 任务执行的顺序:

TASK_COUNT = 15
AsyncTask 0will finish in 5seconds
 AsyncTask 1will finish in 5seconds
 AsyncTask 2will finish in 5seconds
 AsyncTask 3will finish in 5seconds
 AsyncTask 4will finish in 5seconds
===after 5 seconds ...
 AsyncTask 5will finish in 5seconds
 AsyncTask 6will finish in 5seconds
 AsyncTask 7will finish in 5seconds
 AsyncTask 8will finish in 5seconds
 AsyncTask 9will finish in 5seconds
 ===after 5 seconds ...
 AsyncTask 10will finish in 5seconds
 AsyncTask 11will finish in 5seconds
 AsyncTask 12will finish in 5seconds
 AsyncTask 13will finish in 5seconds
 AsyncTask 14will finish in 5seconds
TASK_COUNT = 137
AsyncTask 0will finish in 5seconds
AsyncTask 1will finish in 5seconds
AsyncTask 2will finish in 5seconds
AsyncTask 4will finish in 5seconds
AsyncTask 3will finish in 5seconds
AsyncTask 133will finish in 5seconds
AsyncTask 134will finish in 5seconds
AsyncTask 135will finish in 5seconds
AsyncTask 136will finish in 5seconds
==after 5 seconds
AsyncTask 6will finish in 5seconds
AsyncTask 5will finish in 5seconds
AsyncTask 7will finish in 5seconds
AsyncTask 8will finish in 5seconds
AsyncTask 9will finish in 5seconds
==after 5 seconds
.....
TASK_COUNT = 150
AsyncTask 0will finish in 5seconds
AsyncTask 1will finish in 5seconds
AsyncTask 2will finish in 5seconds
AsyncTask 4will finish in 5seconds
AsyncTask 3will finish in 5seconds
AsyncTask 133will finish in 5seconds
AsyncTask 134will finish in 5seconds
AsyncTask 135will finish in 5seconds
AsyncTask 136will finish in 5seconds
FATAL EXCEPTION: main
Process: com.example.junli.asyncktaskresearch, PID: 1363
java.util.concurrent.RejectedExecutionException: Task android.os.AsyncTask  $3@5288b4b8 rejected from java.util.concurrent.ThreadPoolExecutor@5286a5ec[Running, pool size = 9, active threads = 9, queued tasks = 128, completed tasks = 0

当task_count 等于15时,情况和task_count等于10相同,先取五个任务执行,其余任务如队列,每次取出5个任务执行,

当task_count等于137时,我们发现从133以后的任务没有入队列而是直接执行,结合上述规则,我们可以知道当前等待队列已满,而正在运行的线程已经达到五个,因此可以推算出工作队列的大小为133-5=128个

当task_count等于150时,我们发现第一次会先运行9个任务,max_pool_size = 9, 而当还有任务到达时,系统会抵用RejectExceptionHandler 抛出RejectedExecutionException.

我们到AsyncTask的源码中看看,可以看到如下代码:


private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();
private static final int CORE_POOL_SIZE = CPU_COUNT + 1;
private static final int MAXIMUM_POOL_SIZE = CPU_COUNT * 2 + 1;
private static final int KEEP_ALIVE = 1;
private static final BlockingQueue<Runnable> sPoolWorkQueue =
new LinkedBlockingQueue<Runnable>(128);

本人调试程序所用的设备cpu_count = 4, 因此core_pool_size = 5, maximum_pool_size = 9, 等待工作队列的长度为预设值128。

到了这里,相信大家对asynctask内部的任务调度有了个初步的了解,当然这还是从应用程序层面去了解它,在进一步深究它之前,我们可以先来解决如下几个问题:

默认的execute方式

以上我们研究的都是使用THREAD_POOL_EXECUTOR来execute 任务的,如果我们调用任务默认的execute函数时,情况会有什么不同呢?我们先来将程序改一下:

public void launchAsyncTasks(int number)
    {
        for(int i=0;i< number; ++i)
        {
//            new MyAsyncTask().executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, i);
            new MyAsyncTask().execute(i);
        }
    }

task_count = 10
任务会如何调度呢?来看log:

AsyncTask 0will finish in 5seconds
=== after 5 seconds
AsyncTask 1will finish in 5seconds
=== after 5 seconds
AsyncTask 2will finish in 5seconds
=== after 5 seconds
AsyncTask 4will finish in 5seconds
=== after 5 seconds
...

可以看出每次都只会有一个asyncTask被执行,其余的任务陷入等待, 我们来看看execute(params…)的实现方式:

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

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

我们可以看到,默认的execute方式使用的SerialExecutor方式来执行任务的,顾名思义,任务将顺序执行,每次只会有一个任务在运行,每当一个任务到达,即将任务入队列,当有没有任务在运行时(mActive ==null ),我们调用scheduleNext函数, 从队列mTasks中取出一个任务,交给THREAD_POOL_EXECUTOR.execute去执行,每次任务执行完毕后,scheduleNext又会调用,安排执行下一个任务。

可以自定义Executor吗

之前使用的都是系统为我们定制好的Executor,我们可以自定义自己的Executor吗?我们来尝试创建自己的Executor:

    static Executor mExecutor;
    static final int MY_CORE_SIZE = 3;
    static final int MY_MAXIMUM_SIZE = 6;
    static final int MY_KEEP_ALIVE = 1;
    static BlockingQueue<Runnable> mQueue = new LinkedBlockingQueue<>(12);

    mExecutor = new ThreadPoolExecutor(MY_CORE_SIZE, MY_MAXIMUM_SIZE, MY_KEEP_ALIVE, TimeUnit.SECONDS, mQueue);

当task_count = 10时,log如下:

AsyncTask 0will finish in 5seconds
AsyncTask 1will finish in 5seconds
AsyncTask 2will finish in 5seconds
===after 5 seconds ...
AsyncTask 3will finish in 5seconds
AsyncTask 4will finish in 5seconds
AsyncTask 5will finish in 5seconds
===after 5 seconds ...
AsyncTask 6will finish in 5seconds
AsyncTask 7will finish in 5seconds
AsyncTask 8will finish in 5seconds
===after 5 seconds ...
AsyncTask 9will finish in 5seconds

从log可以看出,每次都只会有3个任务同时在运行。
好了,AsyncTask的原理,相信大家看了上述讲解肯定都有了一定的了解,敬请期待关于AsyncTask的下一篇。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值