线程池是什么?
线程池是一种集中管理多线程的容器,线程使用完不会销毁,会先储存在线程池中,在处理过多任务时会将任务添加到队列中,然后在创建线程后自动启动这些任务。
为什么要使用线程池?
1:减少了创建和销毁线程的次数,每个工作线程都可以被重复利用,可执行多个任务。
2:可以根据系统的承受能力,调整线程池中工作线线程的数目,防止因为消耗过多的内存,而把服务器累趴下(每个线程需要大约1MB内存,线程开的越多,消耗的内存也就越大,最后死机)。
线程池工作流程
创建完线程池后并不会立即创建线程, 而是等到有任务提交时才会创建线程来进行处理。
每当有新任务请求进入线程池中
1:判断核心线程数(corePoolSize)是否饱和,如果不饱和则创建新线程执行任务请求,反之则继续
2:判断工作队列(corePoolSize)是否饱和,如果不饱和则将任务放入工作队列中,反之则继续
3:判断最大线程数(maximumPoolSize)是否饱和,如果不饱和则创建新的线程执行任务请求,反之则执行饱和策略
自定义线程池参数解析
//源码默认至少要传入的参数corePoolSize、maximumPoolSize、keepAliveTime、unit、workQueue
//可以看出
//默认的线程工厂是Executors.defaultThreadFactory()
//默认的饱和策略是AbortPolicy
public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit,
BlockingQueue<Runnable> workQueue) {
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
Executors.defaultThreadFactory(), defaultHandler);
}
private static final RejectedExecutionHandler defaultHandler = new AbortPolicy();
/** 自定义创建线程池
* @param corePoolSize 核心线程数 5
* @param maximumPoolSize 最大线程数 10
* @param keepAliveTime 空闲线程存活时间 60
* @param unit 空闲线程存活时间单位 SECONDS秒
* @param workQueue 工作队列
* @param threadFactory 线程工厂(默认defaultThreadFactory)
* @param handler 处理饱和状态机制(默认AbortPolicy)
*/
ExecutorService customThread = new ThreadPoolExecutor(5,10,60,
TimeUnit.SECONDS,new LinkedBlockingQueue<>(),
Executors.defaultThreadFactory(),
new ThreadPoolExecutor.AbortPolicy());
解析源码七大参数含义:
一:corePoolSize 核心线程数
线程池中最小的线程数量,即使这些线程处理空闲状态且超过了keepAliveTime,他们也不会被销毁,除非设置allowCoreThreadTimeOut。
二:maximumPoolSize 最大线程数
线程池不会无限制的去创建新线程,最大线程数量由maximunPoolSize来决定。
三:keepAliveTime 空闲线程存活时间
一个线程如果处于空闲状态,并且当前的线程数量大于corePoolSize,那么在超过keepAliveTime时间后,这个空闲线程会被销毁。
四:unit 空闲线程存活时间单位
keepAliveTime的时间单位
五:workQueue 工作队列
JAVA中提供了几种类型的workQueue,前三种使用比较广泛
-
ArrayBlockingQueue
有界(数组)队列,遵循FIFO,新任务进来后,会放到队列的队尾。因为没有无参数构造器,必须给定初始队列大小参数。
当线程数量已经达到maxPoolSize,则会执行拒绝策略。 -
LinkedBlockingQuene
无界(链表)队列,遵循FIFO,
//默认容量为Interger.MAX
public LinkedBlockingQueue() {
this(Integer.MAX_VALUE);
}
如果使用无参构造器,当线程池中线程数量达到corePoolSize后,再有新任务进来,由于队列容量是Integer.MAX_VALUE,容量可认为无限大,任务会一直存入该队列,因此使用该工作队列时,参数maxPoolSize其实是不起作用的。如果使用传入容量大小maxPoolSize才能起作用
//也可自定义容量大小
public LinkedBlockingQueue(int capacity) {
if (capacity <= 0) throw new IllegalArgumentException();
this.capacity = capacity;
last = head = new Node<E>(null);
}
- SynchronousQuene
SynchronousQuene不是一个真正意义上的队列,因为SynchronousQuene内部最多只能包含一个元素(也可认为只能包含0个元素,因为包含一个元素后没线程消费进程就会阻塞)。插入元素到队列的线程被阻塞,直到另一个线程从队列中获取了队列中存储的元素。同样,如果线程尝试获取元素并且当前不存在任何元素,则该线程将被阻塞,直到线程将元素插入队列。
public class ThreadDemo {
public static void main(String[] args) throws InterruptedException {
//创建一个SynchronousQueue队列
SynchronousQueue s = new SynchronousQueue();
//创建一个消费线程
Thread t = new Thread() {
@SneakyThrows
@Override
public void run() {
s.take(); //消费SynchronousQueue元素
}
};
t.start(); //必须开启在s.put("666");语句前面,等待SynchronousQueue插入元素,否则进程一直阻塞
s.put("666");
t.join();
System.out.println(s.size()); //最后的结果永远是0,因为只有为null,才不会阻塞
}
}
- PriorityBlockingQueue
优先级的无界阻塞队列,优先级通过参数Comparator实现。
六:threadFactory 线程工厂
创建一个新线程时使用的工厂,可以用来设定线程名、是否为daemon线程等等
七:handler 饱和(拒绝)策略
当工作队列中的任务已到达最大限制,并且线程池中的线程数量也达到最大限制,这时如果有新任务提交进来,就会执行饱和(拒绝)策略,JAVA中提供了4中拒绝策略:
- AbortPolicy(默认)
public static class AbortPolicy implements RejectedExecutionHandler {
public AbortPolicy() { }
// 直接丢弃任务 然后抛出异常
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
throw new RejectedExecutionException("Task " + r.toString() +
" rejected from " +
e.toString());
}
}
- CallerRunsPolicy
public static class CallerRunsPolicy implements RejectedExecutionHandler {
public CallerRunsPolicy() { }
// 判断线程池是否shutdown,则直接抛弃任务,否则执行调用者中被拒绝任务的run方法
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
if (!e.isShutdown()) {
r.run();
}
}
}
- DiscardOldestPolicy
public static class DiscardOldestPolicy implements RejectedExecutionHandler {
public DiscardOldestPolicy() { }
// 抛弃进入队列最早的那个任务,然后尝试把这次拒绝的任务放入队列
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
if (!e.isShutdown()) {
e.getQueue().poll();
e.execute(r);
}
}
}
- DiscardPolicy
public static class DiscardPolicy implements RejectedExecutionHandler {
public DiscardPolicy() { }
// 直接丢弃任务,什么都不做
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {}
}
JAVA自带的四种线程池(Executors类)
一:newCachedThreadPool
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
底层:通过源码可以看出newCachedThreadPool是直接返回一个ThreadPoolExecutor,且相关参数为:
corePoolSize 核心线程数 0
maximumPoolSize 最大线程数 Integer.MAX_VALUE
keepAliveTime 空闲线程存活时间 60
unit 空闲线程存活时间单位 SECONDS
workQueue 工作队列 SynchronousQueue
threadFactory 线程工厂(默认defaultThreadFactory)
handler 处理饱和状态机制(默认AbortPolicy)
线程池解析:通过传入的参数,可以看出这是一个没有核心线程,最大线程数却是Integer.MAX_VALUE(可认为无限大)
当有新任务到来,则插入到SynchronousQueue中,由于SynchronousQueue是同步队列,因此会在池中寻找可用线程来执行,若有可以线程则执行,若没有可用线程则创建一个线程来执行该任务;若池中线程空闲时间超过指定大小,则该线程会被销毁。
适用场景:执行很多短期的任务
二:newFixedThreadPool
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
底层:通过源码可以看出newFixedThreadPool是直接返回ThreadPoolExecutor实例,接收参数为所设定核心线程数量nThreads,且相关参数为:
corePoolSize 核心线程数 nThreads
maximumPoolSize 最大线程数 nThreads
keepAliveTime 空闲线程存活时间 0
unit 空闲线程存活时间单位 MILLISECONDS
workQueue 工作队列 LinkedBlockingQueue
threadFactory 线程工厂(默认defaultThreadFactory)
handler 处理饱和状态机制(默认AbortPolicy)
线程池解析:通过传入的参数,可以看出这是一个固定数量线程的线程池,且都是核心线程,说明存活时间是无限的。
对于多余的任务都放入无界的LinkedBlockingQueue队列中。
适用场景:执行长期任务
三:newScheduledThreadPool
//ScheduledThreadPoolExecutor 类的源码
//可以看到ScheduledThreadPoolExecutor 继承 ThreadPoolExecutor
public class ScheduledThreadPoolExecutor
extends ThreadPoolExecutor
implements ScheduledExecutorService
// Executors 类的源码
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
//调用下面的代码
return new ScheduledThreadPoolExecutor(corePoolSize);
}
//ScheduledThreadPoolExecutor 类的源码
public ScheduledThreadPoolExecutor(int corePoolSize) {
//调用父类(ThreadPoolExecutor)的构造器
super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
new DelayedWorkQueue());
}
底层:通过源码可以看出newScheduledThreadPool是返回ScheduledThreadPoolExecutor封装的ThreadPoolExecutor,接收参数为所设定核心线程数量corePoolSize,DelayedWorkQueue是ScheduledThreadPoolExecutor 静态内部类implement BlockingQueue,且相关参数为:
corePoolSize 核心线程数 corePoolSize
maximumPoolSize 最大线程数 Integer.MAX_VALUE
keepAliveTime 空闲线程存活时间 0
unit 空闲线程存活时间单位 MILLISECONDS
workQueue 工作队列 DelayedWorkQueue
threadFactory 线程工厂(默认defaultThreadFactory)
handler 处理饱和状态机制(默认AbortPolicy)
线程池解析:通过传入的参数,可以看出这是一个无限数量线程的线程池,核心线程存活时间是无限的,
对于keepAliveTime是0,maximumPoolSize设置为 Integer.MAX_VALUE
如果所有线程均处于繁忙状态,对于新任务会进入DelayedWorkQueue队列中,如果队列满了,
就会创建新的线程来执行,执行完,立马销毁。
DelayedWorkQueue是一个会将任务的延时时间进行排序,延时时间少的任务首先被获取的队列
适用场景:执行周期性任务
四:newSingleThreadExecutor
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}
底层:通过源码可以看出newSingleThreadExecutor是返回FinalizableDelegatedExecutorService封装的ThreadPoolExecutor,
且相关参数为:
corePoolSize 核心线程数 1
maximumPoolSize 最大线程数 1
keepAliveTime 空闲线程存活时间 0
unit 空闲线程存活时间单位 MILLISECONDS
workQueue 工作队列 LinkedBlockingQueue
threadFactory 线程工厂(默认defaultThreadFactory)
handler 处理饱和状态机制(默认AbortPolicy)
线程池解析:通过传入的参数,可以看出这是一个只有一个线程的线程池,且存活时间是无限的,工作队列是无界的。
任何任务请求都将放进LinkedBlockingQueue队列里,等待一个线程一个一个取出来执行。
适用场景:一个任务一个任务执行的场景