Android线程池

68 阅读5分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第19天,点击查看活动详情

Android之线程池ThreadPoolExecutor的简介

今天为大家分享一篇关于Android之线程池ThreadPoolExecutor的简介,

Android中的线程池ThreadPoolExecutor解决了单线程下载数据的效率慢和线程阻塞的的问题,它的应用也是优化实现的方式。所以它的重要性不言而喻,但是它的复杂性也大,理解上可能会有问题,不过作为安卓工程师,了解这个也是必然的。

ThreadPoolExecutor有几个构造函数,最多参数的构造函数最常用,下面会详细介绍各个参数的含义及其几个参数之间的关系:

<span style="font-size:18px;">ThreadPoolExecutor(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory, handler)</span>

各个参数的含义:

corePoolSize: 核心线程数,能够同时执行的任务数量

maximumPoolSize:除去缓冲队列中等待的任务,最大能容纳的任务数(其实是包括了核心线程池数量)

keepAliveTime:超出workQueue的等待任务的存活时间maximumPoolSize放置的一个线程的存活时间

unit:时间单位

workQueue:阻塞等待线程的队列,一般使用new LinkedBlockingQueue<Runnable>()这个,如果不指定容量, 会一直往里边添加,没有限制,workQueue永远不会满;

threadFactory:创建线程的工厂,使用系统默认的类

handler:当任务数超过maximumPoolSize时,对任务的处理策略,默认策略是拒绝添加

需要注意的是这里的handler与安卓中的handler不同

执行流程:

当线程数小于corePoolSize时,每添加一个任务,则立即开启线程执行

当corePoolSize满的时候,后面添加的任务将放入缓冲队列workQueue等待;

当workQueue也满的时候,看是否超过maximumPoolSize线程数,如果超过,默认拒绝执行

总结来说:核心线程corePoolSize、任务队列workQueue、最大线程maximumPoolSize,如果三者都满了,使用handler处理被拒绝的任务。当一个线程的存活时间到达指定的存活时间索命该线程的任务已执行完,该线程的生命周期结束,被拒绝的线程可以重新进入线程池中开始任务,依次循环此过程。

线程池的分类

Android中有四类最常见的具有不同功能的线程池,它们通过直接或者间接地配置ThreadPoolExecutor的参数来实现自己的功能特性。 FixedThreadPool

FixedThreadPool通过Executors的newFixedThreadPool方法来创建,是一种线程数固定的线程池。当线程处于空闲状态时,它们并不会被回收,除非线程池被关闭了。

	public static ExecutorService newFixedThreadPool(int nThreads, ThreadFactory threadFactory) {
        return new ThreadPoolExecutor(nThreads, nThreads,
                                      0L, TimeUnit.MILLISECONDS,
                                      new LinkedBlockingQueue<Runnable>(),
                                      threadFactory);
    }

FixedThreadPool的corePoolSize和maximumPoolSize都设置为创建FixedThreadPool指定的参数nThreads,也就意味着FixedThreadPool只有核心线程,并且数量是固定的,没有非核心线程。由于FixedThreadPool只有核心线程并且这些核心线程不会被回收,这就意味着它能够更快速的地响应外界的请求。keepAliveTime设置为0L意味着多余的线程会被立即终止。因为不会产生多余的线程,所以keepAliveTime是无效的参数。另外,任务队列采用了无界的阻塞队列LinkedBlocking Queue。 CachedThreadPool

CachedThreadPool通过Executors的newCachedThreadPool方法来创建,是一种线程数不固定的线程池。

	public static ExecutorService newCachedThreadPool() {
        return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                      60L, TimeUnit.SECONDS,
                                      new SynchronousQueue<Runnable>());
    }

CachedThreadPool的corePoolSize为0,maximumPoolSize设置为Integer.MAX_VALUE,这意味着CachedThreadPool没有核心线程,非核心线程是无界的。keepAliveTime设置为60L,则空闲线程等待新任务的最长时间为 60s。在此用了阻塞队列 SynchronousQueue,它是一个不存储元素的阻塞队列,每个插入操作必须等待另一个线程的移除操作,同样任何一个移除操作都等待另一个线程的插入操作。 ScheduledThreadPool

ScheduledThreadPool是一个能实现定时和周期性任务的线程池。

	public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
        return new ScheduledThreadPoolExecutor(corePoolSize);
    }

这里创建了ScheduledThreadPoolExecutor,ScheduledThreadPoolExecutor继承自ThreadPoolExecutor,它主要用于给定延时之后的运行任务或者定期处理任务。构造方法如下:

	public ScheduledThreadPoolExecutor(int corePoolSize) {
        super(corePoolSize, Integer.MAX_VALUE,
              DEFAULT_KEEPALIVE_MILLIS, MILLISECONDS,
              new DelayedWorkQueue());
    }

ScheduledThreadPoolExecutor 的构造方法最终调用的是ThreadPoolExecutor的构造方法。corePoolSize是传进来的固定数值,maximumPoolSize的值是Integer.MAX_VALUE。因为采用的DelayedWorkQueue是无界的,所以maximumPoolSize这个参数是无效的。 SingleThreadExecutor

SingleThreadExecutor是使用单个工作线程的线程池。

	public static ExecutorService newSingleThreadExecutor() {
        return new FinalizableDelegatedExecutorService
            (new ThreadPoolExecutor(1, 1,
                                    0L, TimeUnit.MILLISECONDS,
                                    new LinkedBlockingQueue<Runnable>()));
    }

corePoolSize和maximumPoolSize都为1,意味着SingleThreadExecutor只有一个核心线程,它确保所有的任务都在同一个线程中按顺序执行。它的意义在于统一所有的外界任务到一个线程中,使这些任务之间不需要处理线程同步问题。其他的参数都和FixedThreadPool一样。

举例说明:

假如:

corePoolSize=2,maximumPoolSize=3,workQueue容量为8;

最开始,执行的任务A,B,此时corePoolSize已用完,再次执行任务C,则C将被放入缓冲队列workQueue中等待着,如果后来又添加了7个任务,此时workQueue已满,则后面再来的任务的数量是5将会和maximumPoolSize比较,由于maximumPoolSize为3,所以只能容纳1个了,因为包含corePollSize个数,所以后面来的任务默认都会被拒绝4个。