Android线程池

线程池一直是一个比较生疏的概念,最近在整理这方面的知识,就把它分享出来。下面分以下三个部分依次展开。

线程池调度

  • 线程池的五种状态



    如上图所示,线程池一共有五种状态,分别是:

RUNNING:可以接受新任务,也可以处理等待队列里的任务。
SHUTDOWN:不接受新任务,但可以处理等待队列里的任务。
STOP:不接受新的任务,不再处理等待队列里的任务。中断正在处理的任务。
TIDYING:所有任务都已经处理完了,当前线程池没有有效的线程,并且即将调用terminated()方法。
TERMINATED:调用了terminated()方法,线程池终止。
另外,ThreadPoolExecutor是用一个AtomicInteger来记录线程池状态和线程池里的线程数量的,如下所示:
低29位:用来存放线程数
高3位:用来存放线程池状态

线程池配置

线程池的配置主要通过构造方法来配置。构造方法如下图:



各个参数的含义:

int corePoolSize:核心线程池大小
int maximumPoolSize:线程池最大容量大小
long keepAliveTime:线程不活动时存活的时间
TimeUnit unit:时间单位
BlockingQueue workQueue:任务队列
ThreadFactory threadFactory:线程工程
RejectedExecutionHandler handler:线程拒绝
策略

  • 如何配置这些参数?
    根据任务特性配置这些参数

任务性质:CPU密集型、IO密集型、混合型
任务优先级:低、中、高
任务执行时间:短、中、长
任务依赖性:是否依赖其他资源,数据库、网络

  • 参数设置

核心线程数corePoolSize与最大线程数maximumPoolSize, 考虑CPU同时执行线程的阈值。一旦超过这个阈值,CPU就需要花费很多 时间来完成线程的切换与调度,这样会导致性能大幅下滑。


keepAliveTime:线程不活动时存活的时间,如果是CPU密集型任务,则将时间设置的小一些,如果是IO密集型或者数据库连接任务,则将时间设置的长一些。

BlockingQueue:描述阻塞队列, 它有以下特点:
1. 不支持null元素
2. 线程安全

  • 实现类
    1. ArrayBlockingQueue :一个数组实现的有界阻塞队列,此队列按照FIFO的原则对元素进行排序,支持公平访问队列(可重入锁实现ReenttrantLock)。
    2. LinkedBlockingQueue :一个由链表结构组成的有界阻塞队列,此队列默认和最大长度为Integer.MAX_VALUE,按照FIFO的原则对元素进行排序。
    3. PriorityBlockingQueue :一个支持优先级排序的无界阻塞队列,默认情况下采用自然顺序排列,也可以指定Comparator。
    4. DelayQueue:一个支持延时获取元素的无界阻塞队列,创建元素时可以指定多久以后才能从队列中获取当前元素,常用于缓存系统设计与定时任务调度等。
    5. SynchronousQueue:一个不存储元素的阻塞队列。存入操作必须等待获取操作,反之亦然,它相当于一个传球手,非常适合传递性场景。
    6. LinkedTransferQueue:一个由链表结构组成的无界阻塞队列,与LinkedBlockingQueue相比多了transfer和tryTranfer方法,该方法在有消费者等待接收元素时会立即将元素传递给消费者。
    7. LinkedBlockingDeque:一个由链表结构组成的双端阻塞队列,可以从队列的两端插入和删除元素。因为出入口都有两个,可以减少一半的竞争。适用于工作窃取的场景。

RejectedExecutionHandler:描述线程数大于或等于线程池最大线程数时的拒绝策略,

  • 实现类
    1. ThreadPoolExecutor.AbortPolicy:默认策略,当线程池中线程的数量大于或者等于最大线程数时,抛出RejectedExecutionException异常。
    2. ThreadPoolExecutor.DiscardPolicy:当线程池中线程的数量大于或者等于最大线程数时,默默丢弃掉不能执行的新任务,不报任何异常。
    3. ThreadPoolExecutor.CallerRunsPolicy:当线程池中线程的数量大于或者等于最大线程数时,如果线程池没有被关闭,则直接在调用者的线程里执行该任务。
    4. ThreadPoolExecutor.DiscardOldestPolicy:当线程池中线程的数量大于或者等于最大线程数时,丢弃阻塞队列头部的任务(即等待最近的任务),然后重新添加当前任务。

Executor提供的工厂方法

  1. newCachedThreadPool():无界可自动回收线程池,查看线程池中有没有以前建立的线程,如果有则复用,如果没有则建立一个新的线程加入池中,池中的线程超过60s不活动则自动终止。适用于生命 周期比较短的异步任务。
  2. newFixedThreadPool(int nThreads):固定大小线程池,与newCachedThreadPool()类似,但是池中持有固定数目的线程,不能随时创建线程,如果创建新线程时,超过了固定 线程数,则放在队列里等待,直到池中的某个线程被移除时,才加入池中。适用于很稳定、很正规的并发线程,多用于服务器。
  3. newScheduledThreadPool(int corePoolSize):周期任务线程池,该线程池的线程可以按照delay依次执行线程,也可以周期执行。
  4. newSingleThreadExecutor():单例线程池,任意时间内池中只有一个线程。

线程池监控

ThreadPoolExecutor里提供了一些空方法,我们可以通过继承ThreadPoolExecutor,复写这些方法来实现对线程池的监控。

  • 代码实现

  • 常见的监控指标有:

    • taskCount:线程池需要执行的任务数量。
    • completedTaskCount:线程池在运行过程中已完成的任务数量,小于或等于taskCount。
    • largestPoolSize:线程池里曾经创建过的最大线程数量。通过这个数据可以知道线程池是否曾经满过。如该数值等于线程池的最大大小,则表示线程池曾经满过。
    • getPoolSize:线程池的线程数量。如果线程池不销毁的话,线程池里的线程不会自动销毁,所以这个大小只增不减。
    • getActiveCount:获取活动的线程数。

以上就是对线程池的总结。

你可能感兴趣的:(Android线程池)