了解android中的线程池

741 阅读5分钟
1.什么是线程池

我们想要使用一个线程的时候,可以创建一个线程,但如果要用多个线程,就要创建多个线程。但线程是一种受限制的系统资源,不可以无限制地产生,而且线程的创建和销毁都会产生相应的开销,如果频繁地创建和销毁线程,会影响系统的效率。这个时候我们就能够使用线程池。线程池会缓存一定量的线程,使得线程可以复用,线程执行完任务之后,并不会销毁,继续执行其他任务。通过线程池可以避免因为频繁创建和销毁线程造成的系统开销。

2.ThreadPoolExecutor

android中的线程池来源于java中的Executor接口,具体实现为 ThreadPoolExecutor类。先看看ThreadPoolExecutor的构造方法:

public class ThreadPoolExecutor extends AbstractExecutorService {
    .....
    public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit,
            BlockingQueue<Runnable> workQueue);
 
    public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit,
            BlockingQueue<Runnable> workQueue,ThreadFactory threadFactory);
 
    public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit,
            BlockingQueue<Runnable> workQueue,RejectedExecutionHandler handler);
 
    public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit,
        BlockingQueue<Runnable> workQueue,ThreadFactory threadFactory,RejectedExecutionHandler handler);
    ...
}

一共有7个参数,先来解析一下每个参数的含义。
int corePoolSize:线程池的核心线程数的最大值
线程池创建新线程的时候,如果当前线程数少于corePoolSize,则会创建核心线程,如果当前线程数大于corePoolSize,则会创建非核心线程。核心线程默认情况下会一直存活在线程池中,但如果将ThreadPoolExecutor 的 allowCoreThreadTimeOut 这个属性为 true,那么闲置的核心线程的存活时间就由keepAliveTime来决定,闲置时间超过keepAliveTime,该闲置的核心线程将会被终止。

int maximumPoolSize:该线程池中线程总数最大值
线程总数 = 核心线程数 + 非核心线程数

long keepAliveTime:非核心线程闲置时的超时时长

TimeUnit unit:keepAliveTime 的单位,是一个枚举
NANOSECONDS : 1微毫秒 = 1微秒 / 1000
MICROSECONDS : 1微秒 = 1毫秒 / 1000
MILLISECONDS : 1毫秒 = 1秒 /1000
SECONDS : 秒
MINUTES : 分
HOURS : 小时
DAYS : 天

BlockingQueue workQueue:该线程池的任务队列
BlockingQueue 又有多个类型

  • ArrayBlockingQueue:可以限定队列的长度,接收到任务的时候,如果没有达到 corePoolSize 的值,则新建线程(核心线程)执行任务,如果达到了,则入队等候,如果队列已满,则新建线程(非核心线程)执行任务,又如果总线程数到了 maximumPoolSize,并且队列也满了,则发生错误

  • LinkedBlockingQueue:这个队列接收到任务的时候,如果当前线程数小于核心线程数,则新建线程(核心线程)处理任务;如果当前线程数等于核心线程数,则进入队列等待。由于这个队列没有最大值限制,即所有超过核心线程数的任务都将被添加到队列中,这也就导致了 maximumPoolSize 的设定失效,因为总线程数永远不会超过 corePoolSize

  • DelayQueue:队列内元素必须实现 Delayed 接口,这就意味着你传进去的任务必须先实现 Delayed 接口。这个队列接收到任务时,首先先入队,只有达到了指定的延时时间,才会执行任务

  • SynchronousQueue:这个队列接收到任务的时候,会直接提交给线程处理,而不保留它,如果所有线程都在工作怎么办?那就新建一个线程来处理这个任务!所以为了保证不出现<线程数达到了 maximumPoolSize 而不能新建线程>的错误,使用这个类型队列的时候,maximumPoolSize 一般指定成 Integer.MAX_VALUE,即无限大

  • PriorityBlockingQueue(不常用):基于优先级的阻塞队列(优先级的判断通过构造函数传入的Compator对象来决定),但需要注意的是PriorityBlockingQueue并不会阻塞数据生产者,而只会在没有可消费的数据时,阻塞数据的消费者。因此使用的时候要特别注意,生产者生产数据的速度绝对不能快于消费者消费数据的速度,否则时间一长,会最终耗尽所有的可用堆内存空间。在实现PriorityBlockingQueue时,内部控制线程同步的锁采用的是公平锁

ThreadFactory threadFactory:线程工厂,为线程池提供创建新线程的功能。

RejectedExecutionHandler handler:当线程池无法执行新任务时,会抛出这个异常

3.线程池的分类

Android中最常见的四类线程池,都是通过配置ThreadPoolExecutor来实现的

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

CachedThreadPool是一种线程数量不定的线程池,只有非核心线程,最大线程数为Integer.MAX_VALUE,有空闲线程则复用空闲线程,若无空闲线程则新建线程,线程池中的空闲线程超时时长为60秒,一定程序减少频繁创建/销毁线程,减少系统开销。
试用方法:

    Runnable runnable = new Runnable() {
        @Override
        public void run() {
            SystemClock.sleep(1000);
        }
    };
    ExecutorService cacheThreadPool = Executors.newCachedThreadPool();
    cacheThreadPool.execute(runnable);
  • FixedThreadPool
    源码:
public static ExecutorService newFixedThreadPool(int nThreads) {
        return new ThreadPoolExecutor(nThreads, nThreads, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>());
}

FixedThreadPool是一种线程数量固定的线程池,只有核心线程并且这些核心线程不会倍回收,可控制线程最大并发数,超出的线程会在队列中等待。
试用方法:

    Runnable runnable = new Runnable() {
        @Override
        public void run() {
            SystemClock.sleep(1000);
        }
    };
    ExecutorService fixedThreadPool= Executors.newFixedThreadPool(4);
    fixedThreadPool.execute(runnable);
  • ScheduledThreadPool
    源码:
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
        return new ScheduledThreadPoolExecutor(corePoolSize);
}

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

ScheduledThreadPool的核心线程是固定的,非核心线程是没有限制的,并且非核心线程闲置时会马上被回收。主要支持定时及周期性任务执行。
试用方法:

    Runnable runnable = new Runnable() {
        @Override
        public void run() {
            SystemClock.sleep(1000);
        }
    };
   ExecutorService scheduledThreadPool =Executors.newScheduledThreadPool(4);
    scheduledThreadPool .execute(runnable);
  • SingleThreadExecutor
    源码:
public static ExecutorService newSingleThreadExecutor() {
        return new FinalizableDelegatedExecutorService
                (new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>()));
}

SingleThreadExecutor只有一个核心线程,所有任务按照指定顺序执行,即遵循队列的入队出队规则。
试用方法:

    Runnable runnable = new Runnable() {
        @Override
        public void run() {
            SystemClock.sleep(1000);
        }
    };
    ExecutorService singleThreadPool =Executors.newSingleThreadPool();
    singleThreadPool .execute(runnable);
参考:

blog.csdn.net/lift_class/…