android 线程池的使用以及Executors的优缺点
2017-12-06 16:59
453 查看
android开发,大家最熟悉的肯定是主线程,也就是ui线程,也都知道在非ui线程更新界面会报错提示不允许在子线程更新ui。但是耗时操作还是需要使用子线程,例如:
感觉方便直接,在任务结束后GC也会自动回收该线程,不过它还在存在很大的弊端
如果某个地方需要开启大量的线程。创建线程和销毁线程是需要时间的,这样会导致性能不足
线程无法管理,相互竞争导致卡顿或者oom
功能太过单一
所以针对这些问题,我们需要重用线程,使用线程池。
线程池:ThreadPoolExecutor
先来看看构造函数:
corepoolsize:核心线程数
maximumPoolSize:最大线程数
keepAliveTime:线程空闲时间
unit:keepAliveTime的时间单位
workQueue:阻塞任务队列
threadFactory:新建线程工厂
RejectedExecutionHandler:当提交当提交任务数超过maxmumPoolSize+workQueue之和时,任务会交给RejectedExecutionHandler来处理
配置起来还是比较繁琐,所以官方提供了一个类Executors来帮助我们迅速创建我们需要的线程池,比较常见的有:
1:newFixedThreadPool() :
该方法返回一个固定线程数量的线程池,该线程池中的线程数量始终不变,即不会再创建新的线程,也不会销毁已经创建好的线程,自始自终都是那几个固定的线程在工作,所以该线程池可以控制线程的最大并发数。 超出的线程会在队列中等待。
2:newCachedThreadPool:
该方法返回一个可以根据实际情况调整线程池中线程的数量的线程池。如果没有线程不够用则会一直创建,有空闲线程则会复用。
3:newSingleThreadExecutor() :
顾名思义,返回只有一个线程的线程池。多余的任务会存在队列中等待执行
4:newScheduledThreadPool():
返回一个可以固定线程个数和设置线程延迟执行时间,执行周期 的线程池
好了,来看下具体使用方式:
newFixedThreadPool
规定线程数为3,然后循环执行打印线程名字
可以看出始终只有三个线程在跑任务,符合预期
newCachedThreadPool
代码差不多,加了一个睡眠时间模拟处理时间,看看控制台部分输出:
因为睡眠时间长导致每次循环都创建一个线程,如果取消sleep时间会看到线程数量没有这么多,因为会复用。
newSingleThreadExecutor
还是差不多的代码,看控制台输出
可以看出每次都是同一个线程在执行,所有任务在排队执行
newScheduledThreadPool
延迟执行,schedule第二三个方法执行时间长度和时间单位
周期性执行,延迟5秒后,每隔3秒执行一次,按照任务开始计算,然后任务执行时间大于间隔时间,则会在任务结束后立马执行下一次。scheduleWithFixedDelay会在任务结束后开始计算间隔时间,和任务执行时间无关.
简单介绍了一下好处,也得来讲讲这种简单创建线程池的方式的缺点:
newFixedThreadPool和newSingleThreadExector:
主要问题是推挤的请求处理队列可能会耗费非常大的内存,甚至导致oom。
newCachedThreadPool和newScheduledThreadPool:
主要问题是线程数没有限制,可能会创建数量非常多的线程,导致oom
new Thread(new Runnable() { @Override public void run() { // TODO Auto-generated method stub } }).start();
感觉方便直接,在任务结束后GC也会自动回收该线程,不过它还在存在很大的弊端
如果某个地方需要开启大量的线程。创建线程和销毁线程是需要时间的,这样会导致性能不足
线程无法管理,相互竞争导致卡顿或者oom
功能太过单一
所以针对这些问题,我们需要重用线程,使用线程池。
线程池:ThreadPoolExecutor
先来看看构造函数:
public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler)
corepoolsize:核心线程数
maximumPoolSize:最大线程数
keepAliveTime:线程空闲时间
unit:keepAliveTime的时间单位
workQueue:阻塞任务队列
threadFactory:新建线程工厂
RejectedExecutionHandler:当提交当提交任务数超过maxmumPoolSize+workQueue之和时,任务会交给RejectedExecutionHandler来处理
配置起来还是比较繁琐,所以官方提供了一个类Executors来帮助我们迅速创建我们需要的线程池,比较常见的有:
1:newFixedThreadPool() :
该方法返回一个固定线程数量的线程池,该线程池中的线程数量始终不变,即不会再创建新的线程,也不会销毁已经创建好的线程,自始自终都是那几个固定的线程在工作,所以该线程池可以控制线程的最大并发数。 超出的线程会在队列中等待。
2:newCachedThreadPool:
该方法返回一个可以根据实际情况调整线程池中线程的数量的线程池。如果没有线程不够用则会一直创建,有空闲线程则会复用。
3:newSingleThreadExecutor() :
顾名思义,返回只有一个线程的线程池。多余的任务会存在队列中等待执行
4:newScheduledThreadPool():
返回一个可以固定线程个数和设置线程延迟执行时间,执行周期 的线程池
好了,来看下具体使用方式:
newFixedThreadPool
ExecutorService executorService = Executors.newFixedThreadPool(3); for (int i = 0; i < 100; i++) { executorService.submit(new Runnable() { @Override public void run() { Log.i("thread", Thread.currentThread().getName()); } }); }
规定线程数为3,然后循环执行打印线程名字
12-06 16:29:06.665 15218-15256/? I/thread: pool-1-thread-1 12-06 16:29:06.667 15218-15256/? I/thread: pool-1-thread-1 12-06 16:29:06.667 15218-15257/? I/thread: pool-1-thread-2 12-06 16:29:06.667 15218-15256/? I/thread: pool-1-thread-1 12-06 16:29:06.667 15218-15258/? I/thread: pool-1-thread-3 12-06 16:29:06.667 15218-15256/? I/thread: pool-1-thread-1 12-06 16:29:06.667 15218-15257/? I/thread: pool-1-thread-2 12-06 16:29:06.667 15218-15256/? I/thread: pool-1-thread-1 12-06 16:29:06.667 15218-15257/? I/thread: pool-1-thread-2 12-06 16:29:06.667 15218-15258/? I/thread: pool-1-thread-3 12-06 16:29:06.667 15218-15257/? I/thread: pool-1-thread-2 12-06 16:29:06.667 15218-15256/? I/thread: pool-1-thread-1 12-06 16:29:06.667 15218-15256/? I/thread: pool-1-thread-1 12-06 16:29:06.667 15218-15256/? I/thread: pool-1-thread-1 12-06 16:29:06.667 15218-15256/? I/thread: pool-1-thread-1 12-06 16:29:06.667 15218-15257/? I/thread: pool-1-thread-2 12-06 16:29:06.667 15218-15256/? I/thread: pool-1-thread-1 12-06 16:29:06.667 15218-15258/? I/thread: pool-1-thread-3 12-06 16:29:06.667 15218-15256/? I/thread: pool-1-thread-1 12-06 16:29:06.667 15218-15256/? I/thread: pool-1-thread-1 12-06 16:29:06.667 15218-15258/? I/thread: pool-1-thread-3 12-06 16:29:06.667 15218-15256/? I/thread: pool-1-thread-1
可以看出始终只有三个线程在跑任务,符合预期
newCachedThreadPool
ExecutorService executorService = Executors.newCachedThreadPool(); for (int i = 0; i < 100; i++) { executorService.submit(new Runnable() { @Override public void run() { Log.i("thread", Thread.currentThread().getName()); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } }); }
代码差不多,加了一个睡眠时间模拟处理时间,看看控制台部分输出:
12-06 16:33:28.463 15429-15454/? I/thread: pool-1-thread-11 12-06 16:33:28.463 15429-15456/? I/thread: pool-1-thread-13 12-06 16:33:28.464 15429-15457/? I/thread: pool-1-thread-14 12-06 16:33:28.464 15429-15458/? I/thread: pool-1-thread-15 12-06 16:33:28.464 15429-15455/? I/thread: pool-1-thread-12 12-06 16:33:28.464 15429-15468/? I/thread: pool-1-thread-25 12-06 16:33:28.464 15429-15469/? I/thread: pool-1-thread-26 12-06 16:33:28.465 15429-15470/? I/thread: pool-1-thread-27 12-06 16:33:28.465 15429-15471/? I/thread: pool-1-thread-28 12-06 16:33:28.466 15429-15472/? I/thread: pool-1-thread-29 12-06 16:33:28.466 15429-15474/? I/thread: pool-1-thread-30 12-06 16:33:28.467 15429-15476/? I/thread: pool-1-thread-32 12-06 16:33:28.467 15429-15475/? I/thread: pool-1-thread-31 12-06 16:33:28.467 15429-15477/? I/thread: pool-1-thread-33 12-06 16:33:28.467 15429-15478/? I/thread: pool-1-thread-34 12-06 16:33:28.468 15429-15479/? I/thread: pool-1-thread-35 12-06 16:33:28.469 15429-15480/? I/thread: pool-1-thread-36 12-06 16:33:28.469 15429-15481/? I/thread: pool-1-thread-37 12-06 16:33:28.469 15429-15482/? I/thread: pool-1-thread-38 12-06 16:33:28.471 15429-15484/? I/thread: pool-1-thread-40 12-06 16:33:28.471 15429-15483/? I/thread: pool-1-thread-39
因为睡眠时间长导致每次循环都创建一个线程,如果取消sleep时间会看到线程数量没有这么多,因为会复用。
newSingleThreadExecutor
ExecutorService executorService = Executors.newSingleThreadExecutor(); for (int i = 0; i < 100; i++) { executorService.submit(new Runnable() { @Override public void run() { Log.i("thread", Thread.currentThread().getName()); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } }); }
还是差不多的代码,看控制台输出
12-06 16:36:30.480 15693-15708/? I/thread: pool-1-thread-1 12-06 16:36:31.481 15693-15708/? I/thread: pool-1-thread-1 12-06 16:36:32.481 15693-15708/? I/thread: pool-1-thread-1 12-06 16:36:33.482 15693-15708/? I/thread: pool-1-thread-1 12-06 16:36:34.482 15693-15708/? I/thread: pool-1-thread-1 12-06 16:36:35.483 15693-15708/? I/thread: pool-1-thread-1 12-06 16:36:36.483 15693-15708/? I/thread: pool-1-thread-1 12-06 16:36:37.484 15693-15708/? I/thread: pool-1-thread-1 12-06 16:36:38.484 15693-15708/? I/thread: pool-1-thread-1 12-06 16:36:39.484 15693-15708/? I/thread: pool-1-thread-1 12-06 16:36:40.485 15693-15708/? I/thread: pool-1-thread-1 12-06 16:36:41.485 15693-15708/? I/thread: pool-1-thread-1
可以看出每次都是同一个线程在执行,所有任务在排队执行
newScheduledThreadPool
ScheduledExecutorService executorService = Executors.newScheduledThreadPool(3); executorService.schedule(new Runnable() { @Override public void run() { Log.i("thread", Thread.currentThread().getName()); } }, 3000, TimeUnit.MILLISECONDS);
延迟执行,schedule第二三个方法执行时间长度和时间单位
ScheduledExecutorService executorService = Executors.newScheduledThreadPool(3); executorService.scheduleAtFixedRate(new Runnable() { @Override public void run() { Log.i("thread", Thread.currentThread().getName()); } }, 5000,3000, TimeUnit.MILLISECONDS);
周期性执行,延迟5秒后,每隔3秒执行一次,按照任务开始计算,然后任务执行时间大于间隔时间,则会在任务结束后立马执行下一次。scheduleWithFixedDelay会在任务结束后开始计算间隔时间,和任务执行时间无关.
简单介绍了一下好处,也得来讲讲这种简单创建线程池的方式的缺点:
newFixedThreadPool和newSingleThreadExector:
主要问题是推挤的请求处理队列可能会耗费非常大的内存,甚至导致oom。
newCachedThreadPool和newScheduledThreadPool:
主要问题是线程数没有限制,可能会创建数量非常多的线程,导致oom
相关文章推荐
- Android线程池(一)——Executors(线程池)以及FutureTask使用示例
- Android线程池(一)——Executors(线程池)以及FutureTask使用示例
- Android中Callable、Future、FutureTask的概念以及几种线程池的使用
- android多线程以及线程池使用
- android 多线程 - 线程池 Executors.newFixedThreadPool 的使用例子
- Android线程池:ExecutorService和Executors使用
- android使用Executors创建线程池的弊端
- 【Android游戏开发二十一】Android os设备谎言分辨率的解决方案!以及简单阐述游戏引擎如何使用!
- Android常用控件-DatePicker以及对话框的两种使用方法
- Android上GTalk以及Push机制的XMPP数据选择使用protobuf格式而非XML格式
- android中APK包的安装以及adb命令的使用
- Android中AVD的使用以及错误处理方法
- android 9.png 的使用 以及自定义按钮
- Android游戏开发之地图编辑器的使用以及绘制地图 (一)
- Android中AVD的使用以及错误处理方法
- android客户端通过Get方式提交参数给服务器,使用URL和HttpURLConnection实现,以及乱码问题解决
- android创建以及使用SDcard镜像文件
- 【Android游戏开发二十一】Android os设备谎言分辨率的解决方案!以及简单阐述游戏引擎如何使用!
- 数据库外键的使用以及优缺点
- [转]Android上GTalk以及Push机制的XMPP数据选择使用protobuf格式而非XML格式