AsyncTask线程池

Android首次引入这个类的时候,异步任务是在单个后台线程上串行执行的,不能并行地执行;在Android1.6开始,AsyncTask引入了线程池,允许多任务并发执行,最大并发数为5,同一时刻最多有5个任务执行,其他任务只能等待,线程池大小为128,如果超过128个任务时,有可能程序崩溃。

在Android 3.0中改正了这个缺陷,采用了2个线程池:一个单线程的线程池SERIAL_EXECUTOR和一个多线程的线程池THREAD_POOL_EXECUTOR,默认情况下任务是串行执行的,但又保留了并发执行任务的特点,用户可以自行配置线程池,执行并发任务需求。
AsyncTask适合执行一些少量任务、不太耗时而且需要及时响应UI的情形,如果需要执行大量耗时的任务而且又要快速响应UI的任务,AsyncTask就不太适合了,因为耗时时间太长其反馈的结果到UI就变得慢,这不符合OS实时响应的特点,所以AsyncTask被认为是轻量级。如果需要执行大量耗时且快速响应UI,最好使用线程池。


看看两个线程池的核心源码:

public static final Executor SERIAL_EXECUTOR = new SerialExecutor();
private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;
    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();
            }
        }

        protected synchronized void scheduleNext() {
            if ((mActive = mTasks.poll()) != null) {
                THREAD_POOL_EXECUTOR.execute(mActive);
            }
        }
    }

SerialExecutor实现了Executor类,重写了execute方法,SerialExecutor内部有一个任务队列ArrayDeque,当有新的任务要执行时,该方法首先把任务放到ArrayDeque队尾,然后判断当前是否有任务在执行,如果没有,就从队首取出一个任务赋值给mActive并交给THREAD_POOL_EXECUTOR线程池执行;如果mActive不为空,表示当前任务正在执行中,此时有新的任务来了就会先放到队列中等待。当正在执行 的任务执行完后,finally语句中的scheduleNext方法会被调用,该方法从队列中取出一个任务执行,这样循环下去就把队列中的所有任务都执行完。

SERIAL_EXECUTOR只是把任务进行排队,并没有执行任务,真正执行任务是线程池THREAD_POOL_EXECUTOR,该线程池提交任务后,该任务Runnable的run方法被调用,run方法中
r.run()-->又会调用r的run,r是一个FutureTask对象,其run方法被调用-->Callable的call方法被调用-->doInBackground
doInBackground具体任务由开发者实现。

再重点看看THREAD_POOL_EXECUTOR线程池的配置:

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 sPoolWorkQueue =
            new LinkedBlockingQueue(128);
			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());
        }
    };
public static final Executor THREAD_POOL_EXECUTOR
		= new ThreadPoolExecutor(CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE,
				TimeUnit.SECONDS, sPoolWorkQueue, sThreadFactory);

CORE_POOL_SIZE:核心线程的数量,此处设为Java虚拟机可获得的有效线程数 + 1,这个值在不同CPU核心数量上有不同的结果,比如4核心cpu的核心线程数量为4+1=5
MAXIMUM_POOL_SIZE:线程池中允许最大的线程数量,此处为Java虚拟机可获得的有效线程数 * 2 +1
KEEP_ALIVE:空闲线程等待新任务的最大时间,超过该时间线程被回收
TimeUnit.SECONDS: KEEP_ALIVE的单位,一般为毫秒或秒
sPoolWorkQueue: 任务等待队列,用来保存将要执行的任务,大小设为128,最多有128个任务在队列中等待,超过该值会出现异常
sThreadFactory:创建线程的工厂对象,每个线程对象的名字以AsyncTask # + 数字的连接字符串命名mCount.getAndIncrement()是原子操作,保证每次读取的int值增加1,线程的名字就会保持唯一
尽管这些配置可以执行多个任务的并发,但由于SERIAL_EXECUTOR线程池的存在,任务的执行过程是仍然串行的,如果想要让任务并行执行,可以采用AsyncTask的executeOnExecutor方法

public final AsyncTask executeOnExecutor(Executor exec,
            Params... params) {

第一个参数表示一个线程池对象,实际上默认情况下这个参数的值就是SERIAL_EXECUTOR。为了让任务并发执行,可以自定义一个线程池,让这个线程池对象作为第一个参数。

Executor exec = new ThreadPoolExecutor(10, 20, 30,
		TimeUnit.SECONDS, new LinkedBlockingQueue());
new CustomAsyncTask().executeOnExecutor(exec);

核心线程设为10个,最大为20个,空闲线程等待时间为30秒,把该线程池对象作为executeOnExecutor的第一个参数,这样就可以灵活运用AsyncTask了。

例1  默认串行执行任务

import android.os.AsyncTask;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.View;
import android.widget.TextView;

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.concurrent.atomic.AtomicInteger;

public class MainActivity extends AppCompatActivity implements View.OnClickListener {
    private static final String TAG = "MainActivity";
    private final AtomicInteger count = new AtomicInteger(1);
    private TextView testButtong;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        testButtong = (TextView) findViewById(R.id.textsi);
        testButtong.setOnClickListener(this);
    }

    @Override
    public void onClick(View v) {
        if (v == testButtong) {
//                Executor customPool = new ThreadPoolExecutor(3, 3, 30,
//                        TimeUnit.SECONDS, new LinkedBlockingQueue());
//                ExecutorService customPool = Executors.newFixedThreadPool(2);
//                for(int i = 0;i<5;i++) {
//                    new MyAsyncTask("AsyncTask #" + count.getAndIncrement()).executeOnExecutor(customPool);
//                }
            for (int i = 0; i < 5; i++) {
                new MyAsyncTask("AsyncTask #" + count.getAndIncrement()).execute("");
            }

        }
    }

    private static class MyAsyncTask extends AsyncTask {
        private final String name;

        public MyAsyncTask(String name) {
            this.name = name;
        }

        @Override
        protected void onPreExecute() {
            super.onPreExecute();
        }

        @Override
        protected String doInBackground(String... strings) {
            Log.d(TAG, " id: " + Thread.currentThread().getId());
            try {
                Thread.sleep(3000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return name;
        }

        @Override
        protected void onPostExecute(String result) {
            super.onPostExecute(result);
            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-mm-dd HH:mm:ss");
            Log.d(TAG, result + " finish at " + sdf.format(new Date()));
        }

        @Override
        protected void onProgressUpdate(Integer... values) {
            super.onProgressUpdate(values);
        }
    }
}

运行结果:

10-26 23:31:08.750 6011-6056/www.zhulf.com.androidpractise D/MainActivity:  id: 244
10-26 23:31:11.755 6011-6011/www.zhulf.com.androidpractise D/MainActivity: AsyncTask #1 finish at 2018-31-26 23:31:11
10-26 23:31:11.755 6011-6057/www.zhulf.com.androidpractise D/MainActivity:  id: 245
10-26 23:31:14.756 6011-6011/www.zhulf.com.androidpractise D/MainActivity: AsyncTask #2 finish at 2018-31-26 23:31:14
10-26 23:31:14.757 6011-6058/www.zhulf.com.androidpractise D/MainActivity:  id: 246
10-26 23:31:17.758 6011-6011/www.zhulf.com.androidpractise D/MainActivity: AsyncTask #3 finish at 2018-31-26 23:31:17
10-26 23:31:17.760 6011-6059/www.zhulf.com.androidpractise D/MainActivity:  id: 247
10-26 23:31:20.761 6011-6011/www.zhulf.com.androidpractise D/MainActivity: AsyncTask #4 finish at 2018-31-26 23:31:20
10-26 23:31:20.762 6011-6060/www.zhulf.com.androidpractise D/MainActivity:  id: 248
10-26 23:31:23.764 6011-6011/www.zhulf.com.androidpractise D/MainActivity: AsyncTask #5 finish at 2018-31-26 23:31:23

创建了5个任务,任务的执行时间相差3秒,由此可知,默认是串行执行的。

如果想要在并行情况下执行耗时任务而且要快速响应UI,可以采用AsyncTask的executeOnExecutor方法,该方法的第一个参数是Executor接口,可以根据需求实现一个线程池对象,比如,把上面的案例修改成并行执行,修改onClick

例2 并行执行任务

    @Override
    public void onClick(View v) {
        if (v == startButton) {
                Executor customPool = new ThreadPoolExecutor(3, 3, 30,
                        TimeUnit.SECONDS, new LinkedBlockingQueue());
                //ExecutorService customPool = Executors.newFixedThreadPool(2);
                for(int i = 0;i<5;i++) {
                    new MyAsyncTask("AsyncTask #" + count.getAndIncrement()).executeOnExecutor(customPool);
                }
//            for (int i = 0; i < 5; i++) {
//                new MyAsyncTask("AsyncTask #" + count.getAndIncrement()).execute("");
//            }

        }
    }

运行结果:

10-26 23:55:49.980 6239-6283/www.zhulf.com.androidpractise D/MainActivity:  id: 247
10-26 23:55:49.985 6239-6284/www.zhulf.com.androidpractise D/MainActivity:  id: 248
10-26 23:55:49.986 6239-6285/www.zhulf.com.androidpractise D/MainActivity:  id: 249
10-26 23:55:52.981 6239-6283/www.zhulf.com.androidpractise D/MainActivity:  id: 247
10-26 23:55:52.982 6239-6239/www.zhulf.com.androidpractise D/MainActivity: AsyncTask #1 finish at 2018-55-26 23:55:52
10-26 23:55:52.985 6239-6284/www.zhulf.com.androidpractise D/MainActivity:  id: 248
10-26 23:55:52.986 6239-6239/www.zhulf.com.androidpractise D/MainActivity: AsyncTask #2 finish at 2018-55-26 23:55:52
10-26 23:55:52.988 6239-6239/www.zhulf.com.androidpractise D/MainActivity: AsyncTask #3 finish at 2018-55-26 23:55:52
10-26 23:55:55.982 6239-6239/www.zhulf.com.androidpractise D/MainActivity: AsyncTask #4 finish at 2018-55-26 23:55:55
10-26 23:55:55.986 6239-6239/www.zhulf.com.androidpractise D/MainActivity: AsyncTask #5 finish at 2018-55-26 23:55:55

创建线程池customPool,核心线程为3,所以前三个任务执行的时间一样都是52秒,这是并行的。也可以采用Executors工厂方法创建常用的线程池对象,或者直接使用AsyncTask中已有的THREAD_POOL_EXECUTOR,这让AsyncTask的可以用在多种场合。

例3 最大并发数测试

假如CPU有4核心,则cpu_count为4,那么最大核心线程数量为2*4+1等于9,阻塞队列中最大容量为128,最多有9+128=137个任务并发执行,如果超过这个数字线程池就会拒绝执行,抛出RejectedExecutionException异常。

    @Override
    public void onClick(View v) {
        if (v == startButton) {
//                Executor customPool = new ThreadPoolExecutor(3, 3, 30,
//                        TimeUnit.SECONDS, new LinkedBlockingQueue());
                //ExecutorService customPool = Executors.newFixedThreadPool(2);
            Executor customPool = AsyncTask.THREAD_POOL_EXECUTOR;
                for(int i = 0;i<137;i++) {
                    new MyAsyncTask("AsyncTask #" + count.getAndIncrement()).executeOnExecutor(customPool);
                }
//            for (int i = 0; i < 5; i++) {
//                new MyAsyncTask("AsyncTask #" + count.getAndIncrement()).execute("");
//            }

        }
    }

for循环最大为137,运行正常,如果修改成138,运行结果:
10-27 02:02:37.968 7860-7903/www.zhulf.com.androidpractise D/MainActivity:  id: 296
10-27 02:02:37.970 7860-7904/www.zhulf.com.androidpractise D/MainActivity:  id: 297
10-27 02:02:37.971 7860-7905/www.zhulf.com.androidpractise D/MainActivity:  id: 298
10-27 02:02:37.974 7860-7906/www.zhulf.com.androidpractise D/MainActivity:  id: 299
10-27 02:02:37.975 7860-7907/www.zhulf.com.androidpractise D/MainActivity:  id: 300
10-27 02:02:37.980 7860-7909/www.zhulf.com.androidpractise D/MainActivity:  id: 302
10-27 02:02:37.980 7860-7908/www.zhulf.com.androidpractise D/MainActivity:  id: 301
10-27 02:02:37.982 7860-7860/www.zhulf.com.androidpractise D/AndroidRuntime: Shutting down VM
10-27 02:02:37.982 7860-7860/www.zhulf.com.androidpractise E/AndroidRuntime: FATAL EXCEPTION: main
                                                                             Process: www.zhulf.com.androidpractise, PID: 7860
                                                                             java.util.concurrent.RejectedExecutionException: Task android.os.AsyncTask$3@5e345ff rejected from java.util.concurrent.ThreadPoolExecutor@352afcc[Running, pool size = 9, active threads = 9, queued tasks = 128, completed tasks = 0]
                                                                                 at java.util.concurrent.ThreadPoolExecutor$AbortPolicy.rejectedExecution(ThreadPoolExecutor.java:2014)
                                                                                 at java.util.concurrent.ThreadPoolExecutor.reject(ThreadPoolExecutor.java:794)
                                                                                 at java.util.concurrent.ThreadPoolExecutor.execute(ThreadPoolExecutor.java:1340)
                                                                                 at android.os.AsyncTask.executeOnExecutor(AsyncTask.java:607)
                                                                                 at www.zhulf.com.androidpractise.MainActivity.onClick(MainActivity.java:39)
                                                                                 at android.view.View.performClick(View.java:5198)
                                                                                 at android.view.View$PerformClick.run(View.java:21147)
                                                                                 at android.os.Handler.handleCallback(Handler.java:739)
                                                                                 at android.os.Handler.dispatchMessage(Handler.java:95)
                                                                                 at android.os.Looper.loop(Looper.java:148)
                                                                                 at android.app.ActivityThread.main(ActivityThread.java:5417)
                                                                                 at java.lang.reflect.Method.invoke(Native Method)
                                                                                 at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:726)
                                                                                 at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:616)
10-27 02:02:37.984 7860-7910/www.zhulf.com.androidpractise D/MainActivity:  id: 303
10-27 02:02:37.986 7860-7911/www.zhulf.com.androidpractise D/MainActivity:  id: 304

出错的原因是最大执行的任务数超过了线程池能够允许的最大线程数,因此,如果并发任务足够多,且又要快速响应UI,就要另外使用线程池实现

你可能感兴趣的:(Android)