深入理解Android线程池
来源:互联网 发布:修改js中的字体颜色 编辑:程序博客网 时间:2024/05/02 00:53
请尊重作者劳动成果,转载请标明原文链接:http://blog.csdn.net/capzhaot/article/details/51727555
1. 使用线程池的优势
在安卓开发中,开启子线程使我们经常接触到的,安卓系统为了App能够流畅的运行,把主线程作为UI-Thread,也就是关于UI绘制相关的动作都必须在主线程中执行,因此,一些耗时操作,比如访问网络,文件操作等等就必须放到子线程中去执行,这就保证了App的页面的流畅性不受耗时操作的影响.
1.1 传统方式开启子线程的缺点:
在开启一个异步任务时,我们最常见的一个写法就是直接new Thread,比如:
new Thread(new Runnable() { @Override public void run() { // task } }).start();
这么写的确很方便,但是缺点也很明显:
- 由于采用匿名的方式,导致该子线程无法被管理.比如停止,查看异步任务的进度等等.
- 如果需要大量执行异步任务,子线程就会被频繁的创建,销毁,导致系统资源消耗增加.
1.2 使用线程池的好处:
- 可以使得线程池中的线程重用,避免因为线程频繁的创建和销毁带来的性能开销.
- 能有效控制线程池的最大并发数,避免大量的线程之间相互抢占系统资源而导致的阻塞.
- 能够对线程进行简单的管理,并提供定时执行以及制定间隔循环等功能.
2. Android中线程池的实现:
Android中线程池的概念来源于Java,核心实现为ThreadPoolExecutor类,该类提供了一系列参数来配置线程池,根据其配置的参数不同,Android中线程池主要分为四类,以实现不同特点的线程池.下面我们先来认识一下ThreadPoolExecutor类,了解其参数如何配置,然后再详细介绍四类特点各异的线程池.
2.1 ThreadPoolExecutor类介绍:
首先看一下其构造函数:
public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler)
corePoolSize
这个参数表示线程池的核心线程数,核心线程即使空闲也会一直保持存活状态,除非你设置了allowCoreThreadTimeOut(核心线程超时属性)为true.此时闲下来的核心线程一旦超过了keepAliveTime指定的时间,核心线程就会被终止.
maximumPoolSize
表示线程池能容纳的最大线程数量,当池中线程达到此数量时,后续的任务将会被阻塞.
keepAliveTime
当池中线程数量超过核心线程数量时,这些多出的线程如果空闲的话,它的存活时间就为keepAliveTime.超过这个时间,该线程就会被终止.
当allowCoreThreadTimeOut属性设定后,keepAliveTime也会作用于核心线程.
unit
指定keepAliveTime的时间单位,常见的有TimeUnit.MILLISECONDS(毫秒),TimeUnit.SECONDS(秒)以及TimeUnit.MINUTES(分钟)等.
workQueue
指定线程池的任务队列,提交的线程会在此队列中排队.
threadFactory
线程工厂,为线程池的任务创建新的线程,他只有一个方法: Thread newThread(Runnable r).
handler
当线程池已满或者无法继续执行新的任务时,这个时候ThreadPoolExecutor会调用handler的rejectedEcecutor方法来通知调用者.这个参数不太常用.
ThreadPoolExecutor线程创建规则:
(1) 如果线程池的中线程数量未达到核心线程的数量,那么会直接启动一个核心线程来执行任务.
(2)如果线程池中的线程数量达到或者超过核心线程的数量,那么任务会被插入到任务队列中排队等待执行.
(3)如果任务无法被插入到任务队列中,这往往是因为队列已满,若此时线程数量未超过线程池规定的最大值,那么会立即启动一个非核心线程来执行任务.
(4)如果池中数量以达到规定的最大值,那么就拒绝执行任务,这个时候ThreadPoolExecutor会调用rejectedEcecution方法来通知调用者.
3. 线程池的分类:
通过对ThreadPoolExecutor参数的不同配置,可以实现Android中四种常见的具有不同功能的线程池,这四类线程池分别是FixedThreadPool,CacheThreadPool,SckeduleThreadPool,以及SingleThreadExecutor.他们可分别通过Executors类中的对应方法来创建.
1. FixedThreadPool(线程数量固定的线程池)
public static ExecutorService newFixedThreadPool(int nThreads) { return new ThreadPoolExecutor(nThreads, nThreads, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>()); }
Features:
- 最大线程数与核心线程数相同,因此该线程池只有核心线程.
- keepAliveTime为0,说明核心线程没有超时机制,会一直保持active状态.
- 任务队列使用LinkedBlockingQueue,表示任务队列的大小没有限制.
通过对ThreadPoolExecutor的配置我们发现,FixedThreadPool线程池只有核心线程(就像一架飞机,只有商务舱),即使当线程处于空闲状态也不会被回收,除非线程池关闭. 当核心线程数量满时,新的任务就会处于等待状态,直到有核心线程空闲下来.
优势:
由于FixedThreadPool只有核心线程并且不会被回收,这意味着它能更快速度响应外界请求.
2. CacheThreadPool(线程数量不定的线程池 )
public static ExecutorService newCachedThreadPool() { return new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS, new SynchronousQueue<Runnable>()); }
Features:
- 没有核心线程,只有非核心线程,并且最大线程数为任意大.(一架全部都是经济舱的飞机…)
- 有超时机制,空闲的线程超过60s就会被回收.
- 当线程池的线程都处于active状态时,线程池会创建新的线程来处理新的任务.
优势:
CacheThreadPool的SynchronousQueue队列是一个非常特殊的队列,基本可以理解为一个无法插入元素的队列.因此,这将导致任何任务都会被立即执行,从这点看,这类线程比较适合执行大量的耗时较少的任务.而当整个线程池都处于闲置状态时,所有的线程都因超时被停止,因此占用系统资源极少.
3. SckeduleThreadPool( 可调度线程池)
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) { return new ScheduledThreadPoolExecutor(corePoolSize); } public ScheduledThreadPoolExecutor(int corePoolSize) { super(corePoolSize, Integer.MAX_VALUE, DEFAULT_KEEPALIVE_MILLIS, MILLISECONDS, new DelayedWorkQueue()); } //super方法 public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue) { this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, Executors.defaultThreadFactory(), defaultHandler); }
Features:
- 核心线程数量固定,非核心线程数量无限制.
由于使用的是DelayedWorkQueue,这类线程池主要用于执行定时任务和具有固定周期的重复任务.
4. SingleThreadExecutor( 单核心线程池)
public static ExecutorService newSingleThreadExecutor() { return new FinalizableDelegatedExecutorService (new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>())); }
Features:
- 内部只有一个核心线程,没有非核心线程,确保所有任务都在同一个线程中按顺序执行.
- SingleThreadExecutor的意义是统一外界所有的线程到一个线程中,保证其顺序性,这使得其不需要处理线程同步的问题.
4. 四种线程池的简单举例:
Runnable r = new Runnable() { @Override public void run() { SystemClock.sleep(1000); } }; ExecutorService fixedThreadPool = Executors.newFixedThreadPool(4); fixedThreadPool.execute(r); ExecutorService cachedThreadPool = Executors.newCachedThreadPool(); cachedThreadPool.execute(r); ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(4); //延时2s后执行 scheduledThreadPool.schedule(r, 2000, TimeUnit.MILLISECONDS); //延时1s后,每隔1s执行一次. scheduledThreadPool.scheduleAtFixedRate(r, 1000, 1000, TimeUnit.MILLISECONDS); ExecutorService singleThreadExecutor = Executors.newSingleThreadExecutor(); singleThreadExecutor.execute(r);
- 深入理解Android线程池
- 线程池深入理解
- 深入理解线程池
- 深入理解线程池
- 深入理解Android异步线程
- 深入理解在Android中线程池的使用
- 深入理解Java线程池
- 深入理解java线程池
- 深入理解Java线程池
- 深入理解java线程池
- 【Linux】深入理解线程池
- 深入理解Java线程池
- 深入理解Java线程池
- Android 线程模型和Looper深入理解
- 深入理解Android中的线程及线程间通信
- 深入理解Java之线程池
- 深入理解Java之线程池
- 深入理解Java之线程池
- Maven - 生命周期
- 使用httpclient必须知道的参数设置及代码写法、存在的风险
- 外同步和自同步
- 各种颜色的RGB值
- 关于java堆栈的理解
- 深入理解Android线程池
- linux 协议栈
- 文本框获取焦点的时候默认值消失,当默认值发生改变时文本框内容为改变后的内容
- 第十二周项目一复数类中的运算符重载二
- Android沉浸式状态栏实现(半透明浮动状态栏)Activity全屏显示
- Android Studio之正确导入SO库
- OpenMesh学习笔记7 网格文件读写
- Nginx 在windows下注册系统服务
- 图片下载缓存处理基础篇