AsyncTask中的线程池

今天碰到Webveiw初始化时候居然跟AsyncTask的线程池有关,做些整理。

1.线程池的历史。

AsyncTask的执行方法有两种execute和executeOnExecutor,前者使用默认线程池,后者要设置线程池,AsyncTask本身提供了两个全局的线程池

SERIAL_EXECUTOR:同步线程池,一次执行一个。

THREAD_POOL_EXECUTOR:异步线程池,一次执行多个。

(1)SERIAL_EXECUTOR的历史:

2.3.3(level 10)之前,使用的默认线程池是THREAD_POOL_EXECUTOR,当时还没有SERIAL_EXECUTOR。3.0(level 11)之后的默认线程池变成了SERIAL_EXECUTOR。

SERIAL_EXECUTOR

版本区间1.0~2.3.32.3.3~now
默认线程池并发同步


(2)THREAD_POOL_EXECUTOR的历史:

4.3之前CORE_POOL_SIZE=5;MAXIMUM_POOL_SIZE=128;队列数是10

4.3之后CORE_POOL_SIZE=CPU_COUNT+1;MAXIMUM_POOL_SIZE=CPU_COUNT*2 + 1;队列数是128

google这样处理最大的利用了手机的cup资源。


如果算上AsyncTask的前身UserTask,UserTask的默认线程池配置是

 private static final int CORE_POOL_SIZE = 1; 
     private static final int MAXIMUM_POOL_SIZE = 10; 
     private static final int KEEP_ALIVE = 10; 
  
     private static final BlockingQueue<Runnable> sWorkQueue = 
             new LinkedBlockingQueue<Runnable>(MAXIMUM_POOL_SIZE);


版本区间1.0~1.41.5~4.34.3~now
corePoolSize15cpuCount+1
maximumPoolSize10128cpuCount*2+1
workQueueSize1010128

简单介绍下几个参数:

corePoolSize:核心池大小,默认线程池初始化线程数为0,当有新任务会创建新线程来执行(即使有空闲线程),当线程池中的线程数目达到corePoolSize后,使用空闲线程来执行,如果没有空闲线程则将任务放入缓存队列。

maximumPoolSize:线程池最大线程数,它表示在线程池中最多能创建多少个线程;

workQueueSize:这个是缓存队列的容量,AsyncTask使用LinkedBlockingQueue来实现,如创建时候new LinkedBlockingQueue(128) 128即为缓存队列容量,LinkedBlockingQueue的默认容量是Integer.MAX_VALUE。

注:

线程池初始化不一定是0,如调用prestartAllCoreThreads()或者prestartCoreThread()方法,可以预先创建线程。

2.线程数量和任务的控制

Asyn cTask中的同步线程池最终实现还是异步那个线程池,线程池用的是 corePoolSize、 maximumPoolSize、 workQueueSize都是一样的。这三个参数控制了线程池的线程数量、任务是否能被接受,任务是否能够马上执行。

(A)线程数 < corePoolSize,缓存队列有空间:加入新任务,无论是否有空闲线程,立马启动新线程执行任务。

          线程数小于corePoolSize,一般情况这种情况队列不可能有缓存的。上面提到过,初始化时线程数为0,这时每执行一个任务,就创建一个线程(即使有空闲线程),直到           线程数量达到corePoolSize。

(B)线程数 = corePoolSize,缓存队列有空间:加入新任务,如果有空闲线程,立马执行,没有空闲线程放入缓存队列

          线程数达到corePoolSize,只要缓存队列还有空间,就不会再次创建新线程了,把新的任务放在队列中排队,等到有空闲线程来执行。

(C)线程数 >= corePoolSize,缓存队列没有空间:加入新任务,则启动新线程执行。

           线程还没有达到maximumPoolSize,有没有缓存队列空间,只能创建线程来执行新任务,此时线程数必然超过corePoolSize。这部分新建的线程,空闲之后是会被摧毁              的,什么之后摧毁,这受到 keepAliveTime控制。

(D)线程数 = maximumPoolSize,缓存队列没有空间: 加入新任务,会被拒绝。
          
          线程数达到了上线,对垒也满了,只能拒绝新加入的任务了,拒绝是跑出异常还是做出特殊响应,这和 拒绝策略有关系。

 keepAliveTime:
默认情况:keepAliveTime看单词的意思就知道,是线程的存活时间,线程大于corePoolSize时,多出来的线程执行任务完毕后,等待keepAliveTime时间后,进入死亡状           态,直到线程池中的线程数不大于corePoolSize,如果:调用allowCoreThreadTimeOut(true)方法,keepAliveTime起作用,直到线程池中的线程数为0;
 keepAliveTime的时间单位有7种取值,在TimeUnit类中有7种静态属性:
              TimeUnit.DAYS;               //天
              TimeUnit.HOURS;             //小时
              TimeUnit.MINUTES;           //分钟
              TimeUnit.SECONDS;           //秒
              TimeUnit.MILLISECONDS;      //毫秒
              TimeUnit.MICROSECONDS;      //微妙
              TimeUnit.NANOSECONDS;       //纳秒

拒绝策略:
          上面有提到过,拒绝任务时候的根据拒绝策略做出不一样的反应,在ThreadPoolExecutor中对应属性是private volatile RejectedExecutionHandler handler;

         ThreadPoolExecutor.AbortPolicy:( ThreadPoolExecutor默认)丢弃任务并抛出RejectedExecutionException异常。
     ThreadPoolExecutor.DiscardPolicy:也是丢弃任务,但是不抛出异常。
 ThreadPoolExecutor.DiscardOldestPolicy:丢弃队列最前面的任务,然后重新尝试执行任务(重复此过程)
 ThreadPoolExecutor.CallerRunsPolicy:由调用线程处理该任务

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


从源码可以看出来,SERIAL_EXECUTOR线程池是在THREAD_POOL_EXECUTOR基础上实现的,使用ArrayDeque双端队列来实现one by one的。

4.AsyncTask与Handler的关系

Handler和AsyncTask都是为了不阻塞主线程(UI线程)将耗时操作异步处理。
 首先有的Handler,Handler提供了主线程和子线程通信的消息机制。

Handler的作用更广泛:
(1)Handler可以延迟在主线程执行一段代码,如post(Runnable)。
(2)Handler+Thread模式,解决UI必须在主线程操作的问题

当任务量大,且需要线程池来管理Thread的时候,google就封装了AsyncTask,Android 1.5 引入AsyncTask,之前叫UserTask。
如果任务量大,AsyncTask是更好的选择。否则还是Handler+Thread更好,因为AsyncTask在子线程和主线程交互的时候(onPostExecute,onProgressUpdate)还是要使用Handler。

   private static class InternalHandler extends Handler {
        public InternalHandler() {
            super(Looper.getMainLooper());
        }

        @SuppressWarnings({"unchecked", "RawUseOfParameterizedType"})
        @Override
        public void handleMessage(Message msg) {
            AsyncTaskResult<?> result = (AsyncTaskResult<?>) msg.obj;
            switch (msg.what) {
                case MESSAGE_POST_RESULT:
                    // There is only one result
                    result.mTask.finish(result.mData[0]);//如果没有cancel 执行onPostExecute
                    break;
                case MESSAGE_POST_PROGRESS:
                    result.mTask.onProgressUpdate(result.mData);//更新进度
                    break;
            }
        }
    }








  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
异步任务(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界面。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值