线程池的作用
- 降低资源消耗。通过重复利用已创建的线程降低线程创建和销毁造成的消耗。
- 可有效控制最大并发线程数,(线程并发数量过多,抢占系统资源从而导致阻塞)
- 提高线程的可管理性。使用线程池可以进行统一的分配,调优和监控。
线程池的启动策略
- 线程池刚创建时,里面没有一个线程。任务队列是作为参数传进来的。不过,就算队列里面有任务,线程池也
不会马上执行它们。 - 当调用 execute() 方法添加一个任务时,线程池会做如下判断:
- 如果正在运行的线程数量小于 corePoolSize,那么马上创建线程运行这个任务;
- 如果正在运行的线程数量大于或等于 corePoolSize,那么将这个任务放入队列。
- 如果这时候队列满了,而且正在运行的线程数量小于 maximumPoolSize,那么还是要创建线程运行这个任务;
- 如果队列满了,而且正在运行的线程数量大于或等于 maximumPoolSize,那么线程池会抛出异常,告诉调用者“我不能再接受任务了”。
- 当一个线程完成任务时,它会从队列中取下一个任务来执行。
- 当一个线程无事可做,超过一定的时间(keepAliveTime)时,线程池会判断,如果当前运行的线程数大于corePoolSize,那么这个线程就被停掉。所以线程池的所有任务完成后,它最终会收缩到 corePoolSize 的大小。
举例说明:
当线程数小于corePoolSize时,每添加一个任务,则立即开启线程执行
当corePoolSize满的时候,后面添加的任务将放入缓冲队列workQueue等待;
当workQueue也满的时候,看是否超过maximumPoolSize线程数,如果超过,默认拒绝执行
假如:corePoolSize=2,maximumPoolSize=3,workQueue容量为8;
最开始,执行的任务A,B,此时corePoolSize已用完,再次执行任务C,则
C将被放入缓冲队列workQueue中等待着,如果后来又添加了7个任务,此时workQueue已满,
则后面再来的任务将会和maximumPoolSize比较,由于maximumPoolSize为3,所以只能容纳1个了,
因为有2个在corePoolSize中运行了,所以后面来的任务默认都会被拒绝。
线程池ThreadPoolExecutor的构造
- int corePoolSize => 该线程池中核心线程数最大值
核心线程:
核心线程数,能够同时执行的任务数量
线程池新建线程的时候,如果当前线程总数小于corePoolSize,则新建的是核心线程,如果超过corePoolSize,则新建的是非核心线程
核心线程默认情况下会一直存活在线程池中,即使这个核心线程啥也不干(闲置状态)。
如果指定ThreadPoolExecutor的allowCoreThreadTimeOut这个属性为true,那么核心线程如果不干活(闲置状态)的话,超过一定时间(时长下面参数决定),就会被销毁掉
很好理解吧,正常情况下你不干活我也养你,因为我总有用到你的时候,但有时候特殊情况(比如我自己都养不起了),那你不干活我就要把你干掉了
- int maximumPoolSize
除去缓冲队列中等待的任务,最大能容纳的任务数(其实是包括了核心线程池数量)
- long keepAliveTime
超出workQueue的等待任务的存活时间,就是指maximumPoolSize里面的等待任务的存活时间
该线程池中非核心线程闲置超时时长
一个非核心线程,如果不干活(闲置状态)的时长超过这个参数所设定的时长,就会被销毁掉
如果设置allowCoreThreadTimeOut = true,则会作用于核心线程
- TimeUnit unit
keepAliveTime的单位,TimeUnit是一个枚举类型,其包括:
NANOSECONDS : 1微毫秒 = 1微秒 / 1000
MICROSECONDS : 1微秒 = 1毫秒 / 1000
MILLISECONDS : 1毫秒 = 1秒 /1000
SECONDS : 秒
MINUTES : 分
HOURS : 小时
DAYS : 天
- BlockingQueue workQueue
该线程池中的任务队列:维护着等待执行的Runnable对象
当所有的核心线程都在干活时,新添加的任务会被添加到这个队列中等待处理,如果队列满了,则新建非核心线程执行任务
常用的workQueue类型:
SynchronousQueue:这个队列接收到任务的时候,会直接提交给线程处理,而不保留它,如果所有线程都在工作怎么办?那就新建一个线程来处理这个任务!所以为了保证不出现<线程数达到了maximumPoolSize而不能新建线程>的错误,使用这个类型队列的时候,maximumPoolSize一般指定成Integer.MAX_VALUE,即无限大
LinkedBlockingQueue:这个队列接收到任务的时候,如果当前线程数小于核心线程数,则新建线程(核心线程)处理任务;如果当前线程数等于核心线程数,则进入队列等待。由于这个队列没有最大值限制,即所有超过核心线程数的任务都将被添加到队列中,这也就导致了maximumPoolSize的设定失效,因为总线程数永远不会超过corePoolSize
ArrayBlockingQueue:可以限定队列的长度,接收到任务的时候,如果没有达到corePoolSize的值,则新建线程(核心线程)执行任务,如果达到了,则入队等候,如果队列已满,则新建线程(非核心线程)执行任务,又如果总线程数到了maximumPoolSize,并且队列也满了,则发生错误
DelayQueue:队列内元素必须实现Delayed接口,这就意味着你传进去的任务必须先实现Delayed接口。这个队列接收到任务时,首先先入队,只有达到了指定的延时时间,才会执行任务
- threadFactory
创建线程的工厂,使用系统默认的类
- handler
当任务数超过maximumPoolSize时,对任务的处理策略,默认策略是拒绝添加
线程池创建示例
public class ThreadPoolManager {
private static ThreadPoolManager mIntance = new ThreadPoolManager();
public static ThreadPoolManager getInstance() {
return mIntance;
}
private int corePoolSize;//核心线程池的数量,能够同时执行的线程数量
private int maximumPoolSize;//最大线程池的数量
private long keepAliveTime = 1;//存活时间
private TimeUnit unit = TimeUnit.HOURS;//时间单位
private ThreadPoolExecutor executor;
private ThreadPoolManager() {
//对corePoolSize进行赋值,算法:当前设备的可用处理器核心数*2 + 1,能够让CPu的效率得到最大程度的发挥
corePoolSize = Runtime.getRuntime().availableProcessors() * 2 + 1;
maximumPoolSize = corePoolSize;//虽然用不到,但是如果为0则会报错
//线程池的机制:领工资机制
executor = new ThreadPoolExecutor(
corePoolSize, //3
maximumPoolSize,//5,当缓冲队列也满的时候会放入最大线程池,但是它是包含了核心线程池数量的
keepAliveTime,
unit,
new LinkedBlockingQueue<Runnable>(), //超出核心线程池的任务将会放入缓冲队列等待着
Executors.defaultThreadFactory(), //创建线程的工厂
new ThreadPoolExecutor.AbortPolicy() //当最大线程池满的时候的处理策略,默认策略是拒绝添加执行
);
}
/**
* 往线程池中添加任务
*
* @param runnable
*/
public void execute(Runnable runnable) {
if (runnable == null) return;
executor.execute(runnable);
}
/**
* 从线程池中移除任务
*
* @param runnable
*/
public void remove(Runnable runnable) {
if (runnable == null) return;
executor.remove(runnable);
}
}
常见的四种线程池
- CachedThreadPool()
可缓存线程池:它的特点- 线程数无限制
- 有空闲线程则复用空闲线程,若无空闲线程则新建线程
- 一定程序减少频繁创建/销毁线程,减少系统开销
创建方法ExecutorService cachedThreadPool = Executors.newCachedThreadPool();
源码:
public static ExecutorService newCachedThreadPool(ThreadFactory threadFactory) {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>(),
threadFactory);
}
- FixedThreadPool()
定长线程池:它的特点- 可控制线程最大并发数(同时执行的线程数)
- 超出的线程会在队列中等待.
创建方法
//nThreads => 最大线程数即maximumPoolSize
ExecutorService fixedThreadPool = Executors.newFixedThreadPool(int nThreads);
源码:
public static ExecutorService newFixedThreadPool(int nThreads, ThreadFactory threadFactory) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>(),
threadFactory);
}
- ScheduledThreadPool()
定时线程池:它的特点- 支持定时及周期性任务执行。
创建方法:
//nThreads => 最大线程数即maximumPoolSize
ExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(int corePoolSize);
源码:
private static final long DEFAULT_KEEPALIVE_MILLIS = 10L;
public ScheduledThreadPoolExecutor(int corePoolSize,
ThreadFactory threadFactory) {
super(corePoolSize, Integer.MAX_VALUE,
DEFAULT_KEEPALIVE_MILLIS, MILLISECONDS,
new DelayedWorkQueue(), threadFactory);
}
- SingleThreadExecutor()
单线程化的线程池:它的特点- 有且仅有一个工作线程执行任务
- 所有任务按照指定顺序执行,即遵循队列的入队出队规则
- 串行执行所有的线程
创建方法:ExecutorService singleThreadPool = Executors.newSingleThreadPool();
源码:
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}