前言:前面说过,Android系统延续了Java的单线程设计模型,因此在开发过程中经常会去开辟新的线程,去处理与UI操作无关的任务。线程是操作系统调度的最小单元,同时线程又是一种受限的系统资源,不能无限制的产生,它的创建与销毁都会产生较多的系统开销。因此,为了方便线程的管理,Android提供了线程的管理机制-Executor线程池。

这里简单复习一下,Android在UI线程进行耗时任务出现ANR的几种情况:

  1. Activity在5秒钟内无法响应屏幕触摸事件或键盘输入事件。
  2. BroadcastReceiver在10秒内没有执行完操作。

同样,Android实现多线程的方式很多这里简单总结一下:

  1. 通过继承Thread类实现多线程(通过Handler消息通信机制和UI线程通信)。
  2. 通过Android内部封装类AsyncTask实现多线程(封装了线程池和Handler)。
  3. 通过IntentService实现多线程(内部采用HandlerThread执行任务)。
  4. 通过HandlerThread实现多线程。

上面的都不是我们今天的重点,在这里就不详细分析了,网上也有很多关于它们的用法,有兴趣的可以去找一下。接下来我们郑重认识一下Android线程池Executor:

首先我们介绍一下线程池的优势:

  • 重用线程池中的线程,避免因为现成的创建和销毁带来的性能开销。
  • 有效控制线程池的最大并发数,避免大量线程之间因为抢占系统资源而导致的阻塞现象。
  • 能对线程进行有效的管理,,并且便于控制线程的执行等功能。
  • 提高任务到来时的响应速度,有效提升效率。

我们都知道Android实现线程池都是通过配置ThreadPoolExecutor实现的,同时Android有四类不同的线程池,不同类型的线程池管理线程的策略也大不相同,我们需要综合衡量,选择最适合我们需求且能保证其执行效率的线程池,这就需要我们对ThreadPoolExecutor的配置了解的很清楚。

  1. 接下来我们看一下ThreadPoolExecutor的构造器,需要怎么去初始化一个ThreadPoolExecutor:
public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime,                     TimeUnit unit, BlockingQueue workQueue, ThreadFactory threadFactory) { }
  • corePoolSize:线程池的核心线程数,默认会一直存活,即使它处于空闲的状态,除非你设置它的超时策略。
  • maximumPoolSize:线程池最大容纳线程数量,当任务数量大于最大线程数量时,后续的任务会被阻塞。
  • keepAliveTime:非核心线程的超时时长,找过这个时间后,非核心线程会被回收。核心线程设置了超时策略后,也会被回收。
  • unit:超时时长的单位。
  • workQueue:线程池中存储任务的任务队列,当有空闲线程的时候,就会从队列中取出任务去执行。
  • threadFactory:为线程池提供创建新线程的功能。

接下来,看一下ThreadPoolExecutor执行任务的流程:

  • 如果线程池中线程数量未达到核心线程数量,立即创建一个核心线程去执行任务。
  • 如果线程数达到了核心线程数量,而未达到设置的总线程数。那么会把当前任务插入到任务队列的队尾。
  • 如果任务队列已满,立即创建一个非核心线程去处理当前任务。
  • 如果任务队列已满,并且线程池中线程数量也达到了线程池容纳线程的最大数量,此时拒绝执行此任务。

线程池的分类:

  • FixedThreadPool:仅有核心线程的线程池(线程在空闲时不会被回收)。
    创建方式和示例用法:
 //设置线程池大小,Runtime.getRuntime().availableProcessors() + 1是当前核心线程数        ExecutorService service = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors() + 1);        //这里设置任务个数为核心线程的3倍,如果按上述FixedThreadPool的定义所说,那么这里应该分3个批次打印出来        int maxTaskCount = 3 * (Runtime.getRuntime().availableProcessors() + 1);        for (int i = 0; i < maxTaskCount; i++) {            final int finalI = i;            service.execute(new Runnable() {                @Override                public void run() {                    try {                        System.out.println("currentTime:" + System.currentTimeMillis() + "-----" + "currentTask" + finalI);                        //每间隔2秒打印一个批次的数据,一共分3批次                        Thread.sleep(2000);                    } catch (InterruptedException e) {                        e.printStackTrace();                    }                }            });        }         try {            Thread.sleep(5000);            System.out.println("---------------------------------------");            //在所有任务结束后,查看线程池线程是否被回收(获取活动的线程个数)            System.out.println("currentThreadCount:" + ((ThreadPoolExecutor) service).getActiveCount());        } catch (InterruptedException e) {            e.printStackTrace();        }
打印结果为:
03-12 10:22:56.219 13147-13159/com.hbandroid.testthreaddemo I/System.out: currentTime:1520821376224-----currentTask003-12 10:22:56.219 13147-13160/com.hbandroid.testthreaddemo I/System.out: currentTime:1520821376224-----currentTask103-12 10:22:56.223 13147-13161/com.hbandroid.testthreaddemo I/System.out: currentTime:1520821376225-----currentTask203-12 10:22:56.223 13147-13162/com.hbandroid.testthreaddemo I/System.out: currentTime:1520821376225-----currentTask303-12 10:22:56.223 13147-13163/com.hbandroid.testthreaddemo I/System.out: currentTime:1520821376225-----currentTask403-12 10:22:58.223 13147-13159/com.hbandroid.testthreaddemo I/System.out: currentTime:1520821378225-----currentTask503-12 10:22:58.223 13147-13160/com.hbandroid.testthreaddemo I/System.out: currentTime:1520821378225-----currentTask603-12 10:22:58.223 13147-13161/com.hbandroid.testthreaddemo I/System.out: currentTime:1520821378225-----currentTask703-12 10:22:58.223 13147-13162/com.hbandroid.testthreaddemo I/System.out: currentTime:1520821378225-----currentTask803-12 10:22:58.223 13147-13163/com.hbandroid.testthreaddemo I/System.out: currentTime:1520821378226-----currentTask903-12 10:23:00.223 13147-13161/com.hbandroid.testthreaddemo I/System.out: currentTime:1520821380226-----currentTask1103-12 10:23:00.223 13147-13163/com.hbandroid.testthreaddemo I/System.out: currentTime:1520821380227-----currentTask1203-12 10:23:00.223 13147-13160/com.hbandroid.testthreaddemo I/System.out: currentTime:1520821380226-----currentTask1003-12 10:23:00.223 13147-13159/com.hbandroid.testthreaddemo I/System.out: currentTime:1520821380227-----currentTask1303-12 10:23:00.223 13147-13162/com.hbandroid.testthreaddemo I/System.out: currentTime:1520821380227-----currentTask1403-12 10:38:20.955 13582-13582/com.hbandroid.testthreaddemo I/System.out: ---------------------------------------03-12 10:38:20.955 13582-13582/com.hbandroid.testthreaddemo I/System.out: currentThreadCount:5

结果总结:从上面的结果可以看出,task0-task4打印时间1520821376224时间进行,当间隔2秒过后(即1520821378225时间点),开始执行task5-task9。因此,FixedThreadPool仅有5个核心线程,当任务到来时,能够很快的响应。同时,在所有任务都结束后,线程池活动的线程个数还是5个,说明,在空闲时这些线程不会被回收。

  • CachedThreadPool:仅有非核心线程且线程数量不定的线程池(当任务到来时,有空闲线程,就立即交由该线程去处理任务,否则就立即创建新线程去处理该任务,保证第一时间执行任务。但空闲线程有超时机制,空闲超过60秒,就会被系统回收)。
    创建方式和示例用法:
  ExecutorService service = Executors.newCachedThreadPool();        //设置任务个数为Runtime.getRuntime().availableProcessors() + 1的3倍,即当前核心线程数的3倍        int maxTaskCount = 3 * (Runtime.getRuntime().availableProcessors() + 1);        //如果按照CachedThreadPool的定义,一开始就会创建任务个数的线程个数,去执行所有任务,任务执行完后,线程会被回收        for (int i = 0; i < maxTaskCount; i++) {            final int finalI = i;            service.execute(new Runnable() {                @Override                public void run() {                    try {                        System.out.println("currentTime:" + System.currentTimeMillis() + "-----" + "currentTask" + finalI);                        Thread.sleep(2000);                    } catch (InterruptedException e) {                        e.printStackTrace();                    }                }            });        }        try {            Thread.sleep(5000);            System.out.println("---------------------------------------");            System.out.println("currentThreadCount:" + ((ThreadPoolExecutor) service).getActiveCount());        } catch (InterruptedException e) {            e.printStackTrace();        }

打印结果为:

03-12 10:45:30.179 13769-13782/? I/System.out: currentTime:1520822730182-----currentTask003-12 10:45:30.179 13769-13784/? I/System.out: currentTime:1520822730182-----currentTask203-12 10:45:30.179 13769-13785/? I/System.out: currentTime:1520822730183-----currentTask303-12 10:45:30.179 13769-13786/? I/System.out: currentTime:1520822730183-----currentTask403-12 10:45:30.179 13769-13787/? I/System.out: currentTime:1520822730183-----currentTask503-12 10:45:30.179 13769-13788/? I/System.out: currentTime:1520822730183-----currentTask603-12 10:45:30.179 13769-13783/? I/System.out: currentTime:1520822730182-----currentTask103-12 10:45:30.179 13769-13789/? I/System.out: currentTime:1520822730183-----currentTask703-12 10:45:30.179 13769-13790/? I/System.out: currentTime:1520822730183-----currentTask803-12 10:45:30.179 13769-13791/? I/System.out: currentTime:1520822730184-----currentTask903-12 10:45:30.179 13769-13792/? I/System.out: currentTime:1520822730184-----currentTask1003-12 10:45:30.183 13769-13793/? I/System.out: currentTime:1520822730184-----currentTask1103-12 10:45:30.183 13769-13794/? I/System.out: currentTime:1520822730185-----currentTask1203-12 10:45:30.183 13769-13795/? I/System.out: currentTime:1520822730185-----currentTask1303-12 10:45:30.183 13769-13796/? I/System.out: currentTime:1520822730185-----currentTask1403-12 10:45:35.183 13769-13769/com.hbandroid.testthreaddemo I/System.out: ---------------------------------------03-12 10:45:35.183 13769-13769/com.hbandroid.testthreaddemo I/System.out: currentThreadCount:0

结果总结:从结果可以看出,所有任务机会再同一个时间点开始执行(1520822730182毫秒左右)。可以证明,当前线程池的线程个数可以无限大,当任务到来时,直接创建线程去处理。当任务执行完后,可以看出当时的活动线程个数为0,证明当前线程池的线程在空闲时会被回收。

  • ScheduledThreadPool:拥有固定数量核心线程数量和不固定的非核心线程数量的线程池(非核心线程在空闲时会立即被回收)。
    创建方式和示例用法:
 //设置核心线程个数为Runtime.getRuntime().availableProcessors() + 1        ScheduledExecutorService service = Executors.newScheduledThreadPool(Runtime.getRuntime().availableProcessors() + 1);        //设置任务个数为Runtime.getRuntime().availableProcessors() + 1的3倍,即当前核心线程数的3倍        int maxTaskCount = 3 * (Runtime.getRuntime().availableProcessors() + 1);        for (int i = 0; i < maxTaskCount; i++) {            final int finalI = i;            //执行定时任务,设置2秒后开始开始执行任务            service.schedule(new Runnable() {                @Override                public void run() {                    System.out.println("currentTime:" + System.currentTimeMillis() + "-----" + "currentTask" + finalI);                }            }, 2, TimeUnit.SECONDS);        }

打印结果为:

03-12 11:25:38.923 14758-14770/com.hbandroid.testthreaddemo I/System.out: currentTime:1520825138926-----currentTask003-12 11:25:38.923 14758-14770/com.hbandroid.testthreaddemo I/System.out: currentTime:1520825138926-----currentTask203-12 11:25:38.923 14758-14770/com.hbandroid.testthreaddemo I/System.out: currentTime:1520825138926-----currentTask303-12 11:25:38.923 14758-14770/com.hbandroid.testthreaddemo I/System.out: currentTime:1520825138926-----currentTask403-12 11:25:38.923 14758-14770/com.hbandroid.testthreaddemo I/System.out: currentTime:1520825138926-----currentTask503-12 11:25:38.923 14758-14770/com.hbandroid.testthreaddemo I/System.out: currentTime:1520825138926-----currentTask603-12 11:25:38.923 14758-14770/com.hbandroid.testthreaddemo I/System.out: currentTime:1520825138926-----currentTask703-12 11:25:38.923 14758-14770/com.hbandroid.testthreaddemo I/System.out: currentTime:1520825138927-----currentTask803-12 11:25:38.923 14758-14770/com.hbandroid.testthreaddemo I/System.out: currentTime:1520825138927-----currentTask903-12 11:25:38.923 14758-14770/com.hbandroid.testthreaddemo I/System.out: currentTime:1520825138927-----currentTask1003-12 11:25:38.923 14758-14770/com.hbandroid.testthreaddemo I/System.out: currentTime:1520825138927-----currentTask1103-12 11:25:38.923 14758-14770/com.hbandroid.testthreaddemo I/System.out: currentTime:1520825138927-----currentTask1203-12 11:25:38.923 14758-14770/com.hbandroid.testthreaddemo I/System.out: currentTime:1520825138927-----currentTask1303-12 11:25:38.923 14758-14770/com.hbandroid.testthreaddemo I/System.out: currentTime:1520825138927-----currentTask1403-12 11:25:38.923 14758-14771/com.hbandroid.testthreaddemo I/System.out: currentTime:1520825138926-----currentTask1

结果总结:从上面结果可以看出,我们设置的核心线程数为总任务数的1/3,而结果中所有的任务几乎是在同时执行的(2秒后),可以看出除核心线程外,另外开启了10个非核心线程一起去执行任务。

  • SingleThreadExecutor:仅有一个核心线程的线程池(任务到来时,自动加入任务队列,核心线程空闲时按顺序执行任务)。
    创建方式和示例用法:
 final ExecutorService service = Executors.newSingleThreadExecutor();        //设置任务个数为Runtime.getRuntime().availableProcessors() + 1的3倍,即当前核心线程数的3倍        int maxTaskCount = 3 * (Runtime.getRuntime().availableProcessors() + 1);        for (int i = 0; i < maxTaskCount; i++) {            final int finalI = i;            //每间隔1秒执行一个任务,按照SingleThreadExecutor的特性,会将任务依次执行            service.execute(new Runnable() {                @Override                public void run() {                    try {                        Thread.sleep(1000);                        System.out.println("currentTime:" + System.currentTimeMillis() + "-----" + "currentTask" + finalI);                    } catch (InterruptedException e) {                        e.printStackTrace();                    }                }            });        }        //获取当前线程池活动线程数        try {            Thread.sleep(20000);            System.out.println("---------------------------------------");            System.out.println("currentThreadCount:" + ((ThreadPoolExecutor) service).getActiveCount());        } catch (InterruptedException e) {            e.printStackTrace();        }

打印结果:

03-12 11:47:51.919 15902-15914/com.hbandroid.testthreaddemo I/System.out: currentTime:1520826471921-----currentTask003-12 11:47:52.919 15902-15914/com.hbandroid.testthreaddemo I/System.out: currentTime:1520826472921-----currentTask103-12 11:47:53.919 15902-15914/com.hbandroid.testthreaddemo I/System.out: currentTime:1520826473923-----currentTask203-12 11:47:54.919 15902-15914/com.hbandroid.testthreaddemo I/System.out: currentTime:1520826474924-----currentTask303-12 11:47:55.923 15902-15914/com.hbandroid.testthreaddemo I/System.out: currentTime:1520826475924-----currentTask403-12 11:47:56.923 15902-15914/com.hbandroid.testthreaddemo I/System.out: currentTime:1520826476925-----currentTask503-12 11:47:57.923 15902-15914/com.hbandroid.testthreaddemo I/System.out: currentTime:1520826477926-----currentTask603-12 11:47:58.923 15902-15914/com.hbandroid.testthreaddemo I/System.out: currentTime:1520826478926-----currentTask703-12 11:47:59.927 15902-15914/com.hbandroid.testthreaddemo I/System.out: currentTime:1520826479928-----currentTask803-12 11:48:00.927 15902-15914/com.hbandroid.testthreaddemo I/System.out: currentTime:1520826480932-----currentTask903-12 11:48:01.931 15902-15914/com.hbandroid.testthreaddemo I/System.out: currentTime:1520826481932-----currentTask1003-12 11:48:02.931 15902-15914/com.hbandroid.testthreaddemo I/System.out: currentTime:1520826482933-----currentTask1103-12 11:48:03.931 15902-15914/com.hbandroid.testthreaddemo I/System.out: currentTime:1520826483934-----currentTask1203-12 11:48:04.931 15902-15914/com.hbandroid.testthreaddemo I/System.out: currentTime:1520826484934-----currentTask1303-12 11:48:05.931 15902-15914/com.hbandroid.testthreaddemo I/System.out: currentTime:1520826485936-----currentTask14

结果分析:从上面打印结果可以看出,每一个任务执行的间隔时间都是1秒。证明了SingleThreadExecutor是一个仅有一个核心线程的线程池,每一个任务都会顺序执行。

总结:其实Android实现线程池功能的方法有多种,我们也可以自定义线程池,实现我们想要的功能,但是可能会出现各种意想不到的Bug。因此,建议在实现线程池功能的时候,先想清楚自己想实现的功能,然后看看这几种内置线程池是否适合你的需求,或者稍加改善就能适合你的需求。会避免很多隐式的Bug。

更多相关文章

  1. Android异步任务AsyncTask的使用与原理分析
  2. Android中TCP/IP 长连接 问题以及解决方案
  3. Android-使用mediarecorder类获取当前麦克风音量值
  4. 【Android】Handler使用入门
  5. 更新操作系统Google 修正 Android(安卓)碎片化新法:多更新组件少
  6. android 短信 轰炸机 app 开发 记录 (2)
  7. Android(安卓)信号处理面面观 之 trace 文件含义
  8. 处女男学Android(三)---Handler简介以及初步应用
  9. Android(安卓)Handler 的基本用法

随机推荐

  1. Linux进程通信之匿名管道
  2. ubuntu14.04下qemu调试linux内核
  3. 转:6410下点亮led
  4. Linux常用命令汇总-速查
  5. Linux 3.4.39内核编译配置选项介绍
  6. linux shell脚本编程笔记(四): 获取字符串长
  7. linux apache安装https证书
  8. 有什么办法可以在Windows上不用使用太繁
  9. 线程同步-生产者消费者问题
  10. linux下touch命令也可以一次创建多个文件