因为有了线程池的存在,我现在都很少通过new Thread创建线程执行任务,一般都是直接new Runnable直接扔进线程池去执行,除非我对Thread有十足的把握能控制住(执行、中断、销毁)。

线程池的好处

(1)重用存在的线程,减少对象创建、消亡的开销,性能佳。这个池机制的好处。
(2)可有效控制最大并发线程数,提高系统资源的使用率,同时避免过多资源竞争,避免堵塞。
(3)提供定时执行、定期执行、单线程、并发数控制等功能。

线程池概述

Android的线程池都是通过配置ThreadPoolExecutor来实现的,来,我们看看ThreadPoolExecutor的构造函数:

public ThreadPoolExecutor(int corePoolSize,  
                           int maximumPoolSize,  
                           long keepAliveTime,  
                           TimeUnit unit,  
                           BlockingQueue<Runnable> workQueue,  
                           ThreadFactory threadFactory) {  
    this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,  
         threadFactory, defaultHandler);  
}

我们分析一下每个参数的作用:
corePoolSize: 线程池的核心线程数。默认情况,核心线程会一直在线程池中存在,即使处于闲置的状态。除非设置ThreadPoolExecutor的allowCoreThreadTimeOut属性为true,此时,闲置的核心线程如果在keepAliveTime时间内没有新的任务要执行,就会被销毁。

maximumPoolSize:线程池最大线程数。如果线程池达到最大线程数,如果有新的任务进来,可能会加入队列,如果队列也满了,可能会阻塞或者直接拒绝。

keepAliveTime: 非核心线程闲置时的等待时间。如果非核心线程闲置了,在keepAliveTime时间内,没有新的任务到来,那么这个非核心就会被销毁。如果设置ThreadPoolExecutor的allowCoreThreadTimeOut属性为true,同样对核心线程生效。

unit: keepAliveTime的时间单位,有以下几种:
TimeUnit.MILLISECONDS; // 毫秒
TimeUnit.SECONDS; // 秒
TimeUnit.MINUTES; // 分钟
TimeUnit.HOURS; //小时
TimeUnit.DAYS; //天

workQueue: 线程池的任务队列,通过ThreadPoolExecutor的execute方法提交的Runnable任务都会储存在这个队列里面,等待执行。

threadFactory: 线程工厂,主要提供创建新的线程的功能。ThreadFactory是一个接口,只有一个方法:Thread newThread(Runnable r)。

还有一个不常用的参数,RejectedExecutionHandler handler。这个参数的作用当线程池无法在继续执行新任务时,这可能是任务队列满了或者是无法成功执行任务,此时会调用RejectedExecutionHandler的rejectedExecutoion()来通知调用者拒绝任务。知道就好。

再来说说线程池执行任务的规则/流程(重点):

(1)当通过ThreadPoolExecutor的execute方法提交的Runnable任务时,先会判断线程池中的线程是否超过核心线程数量corePoolSize。如果没有超过,则新建一个核心线程执行Runnable任务。如果超过,转到(2)。

(2)如果线程池的线程超过核心线程数corePoolSize,先来的Runnable任务会加入workQueue队列中,如果队列也已经满了,转到(3)。

(3)如果队列也满了,如果此时线程池的线程数量没有达到最大线程数量maximumPoolSize,那么启动一个非核心线程执行队列任务。如果超过了,转到(4)。

(4)队列也满了,线程池的线程数也达到了最大线程数量maximumPoolSize,此时就会调用RejectedExecutionHandler的rejectedExecutoion()来通知调用者拒绝任务。

android 存储 线程安全 android线程池详解_线程

其实Android已经为我们提供4种线程池使用:
(1)FixedThreadPool

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

这是一种固定数量为nThreads的线程池,没有非核心线程,无限队列,所以不会拒绝新的任务,如果有闲置的线程,则执行任务,没有,新的任务会被加入队列中,等待被执行。

(2)CachedThreadPool

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

没有核心进程,全部任务都会加入SynchronousQueue队列中,因为最大线程数为Integer.MAX_VALUE,说明非核心线程可以很多,所以一旦新的任务加入SynchronousQueue队列,就会创建新的非核心线程去执行,所以可以执行“无限”个任务。非核心线程的闲置时间的60秒,一旦超过60秒没有执行新的任务,就会被销毁。
再解析一下SynchronousQueue (同步队列)。这种队列内部不会存储元素, 每一次插入操作都会先进入阻塞状态,一直等到另一个线程执行了删除操作,然后该插入操作才会执行。同样地,每一次删除操作也都会先进入阻塞状态,一直等到另一个线程执行了插入操作,然后该删除操作才会执行。 当提交一个任务到包含这种 SynchronousQueue 队列的线程池以后,线程池会去检测是否有可用的空闲线程来执行该任务,如果没有就直接新建一个线程来执行该任务而不是将该任务先暂存在队列中。

(3)ScheduledThreadPool

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

有核心线程数,最大线程数为Integer.MAX_VALUE。一旦非核心线程闲置,立刻被销毁。可以执行定时任务和具有固定周期的重复任务。

(4)SingleThreadExecutor

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

核心线程数和最大线程数为1,保证只有一个线程执行任务,确保所有的任务都按顺序执行。任务队列没有限制,可以执行任意多个任务。

线程池源码解析

请参考Java 线程池 ThreadPoolExecutor 源码分析