Java线程池的创建及参数理解
线程池的创建
使用阿里巴巴推荐的创建线程池的方式,通过ThreadPoolExecutor构造函数自定义参数创建。
private static final int CORE_POOL_SIZE = 5;
private static final int MAX_POOL_SIZE = 10;
private static final int QUEUE_CAPACITY = 100;
private static final Long KEEP_ALIVE_TIME = 1L;
public static void main(String[] args) {
ThreadPoolExecutor executor = new ThreadPoolExecutor(
CORE_POOL_SIZE,
MAX_POOL_SIZE,
KEEP_ALIVE_TIME,
TimeUnit.SECONDS,
new ArrayBlockingQueue<>(QUEUE_CAPACITY),
new ThreadPoolExecutor.CallerRunsPolicy());
for (int i = 0; i < size; i++) {
int num = i;
// 1. 第一种写法
//executor.submit(new ThreadClass(num));
// 2. 第二种写法
//executor.submit(new Runnable() {
// @Override
// public void run() {
// System.out.println(Thread.currentThread().getName() + ", " + num);
// }
//});
// 3. lambda写法
executor.submit(() -> {
try {
Thread.sleep(1000);
System.out.println(Thread.currentThread().getName() + ", " + num);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
// 一定要写在 finally中,防止异常计数器未减1
// 使latch的值减1,如果减到了0,则会唤醒所有等待在这个latch上的线程
countDownLatch.countDown();
}
});
}
//终止线程池
executor.shutdown();
try {
executor.awaitTermination(5, TimeUnit.SECONDS);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Finished all threads");
}
/**
* Runnable 类
*/
static class ThreadClass implements Runnable {
private int i;
public ThreadClass(int i) {
this.i = i;
}
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + " star " + i);
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " end " + i);
}
}
创建线程池的构造方法的参数
java.util.concurrent.ThreadPoolExecutor#ThreadPoolExecutor:
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler) {
if (corePoolSize < 0 || maximumPoolSize <= 0 || maximumPoolSize < corePoolSize || keepAliveTime < 0)
throw new IllegalArgumentException();
if (workQueue == null || threadFactory == null || handler == null)
throw new NullPointerException();
this.acc = System.getSecurityManager() == null ? null : AccessController.getContext();
this.corePoolSize = corePoolSize;
this.maximumPoolSize = maximumPoolSize;
this.workQueue = workQueue;
this.keepAliveTime = unit.toNanos(keepAliveTime);
this.threadFactory = threadFactory;
this.handler = handler;
}
创建线程池一共有7个参数,从源码可知,corePoolSize和maximumPoolSize都不能小于0,且核心线程数不能大于最大线程数。
参数用途:
corePoolSize
核心线程数,判断任务没有超过核心线程数则直接创建线程,核心线程不会被回收,即使没有任务执行,也会保持空闲状态。
maximumPoolSize
最大线程数,如果线程池内部线程数已经超过核心线程数,且队列已满,没有超过最大线程数则继续创建新线程,超过最大线程数则出发拒绝策略handler。
keepAliveTime
当线程池内部的 临时线程
数量大于corePoolSize,则多出来的线程会在keepAliveTime时间之后销毁。
临时线程
是指当已经达到核心线程数、队列已满,且未达到最大核心数而创建的线程。
unit
keepAliveTime 的时间单位
workQueue
队列,当前线程数超过corePoolSize时,新的任务会处在等待状态,并存在workQueue中。
threadFactory
创建线程的工厂类,通常我们会自顶一个threadFactory设置线程的名称,这样我们就可以知道线程是由哪个工厂类创建的,可以快速定位。
handler
任务拒绝策略,当最大线程数已满,且任务队列已满,又有新的任务进来时,会调用handler拒绝策略来处理请求。
系统默认的拒绝策略有以下几种:
- AbortPolicy:为线程池默认的拒绝策略,该策略直接抛异常处理。
- DiscardPolicy:直接抛弃不处理。
- DiscardOldestPolicy:丢弃队列中最老的任务。
- CallerRunsPolicy:将任务分配给当前执行execute方法线程来处理。
- 自定义拒绝策略,只需要实现RejectedExecutionHandler接口即可
我们还可以自定义拒绝策略,只需要实现RejectedExecutionHandler接口即可,友好的拒绝策略实现有如下:
将数据保存到数据,待系统空闲时再进行处理
将数据用日志进行记录,后由人工处理
为什么不建议使用Executors创建线程池?
JDK为我们提供了Executors线程池工具类,里面有默认的线程池创建策略,大概有以下几种:
FixedThreadPool:线程池线程数量固定,即corePoolSize和maximumPoolSize数量一样。
SingleThreadPool:单个线程的线程池。
CachedThreadPool:初始核心线程数量为0,最大线程数量为Integer.MAX_VALUE,线程空闲时存活时间为60秒,并且它的阻塞队列为SynchronousQueue,它的初始长度为0,这会导致任务每次进来都会创建线程来执行,在线程空闲时,存活时间到了又会释放线程资源。
ScheduledThreadPool:创建一个定长的线程池,而且支持定时的以及周期性的任务执行,类似于Timer。
用Executors工具类虽然很方便,依然不推荐大家使用以上默认的线程池创建策略,阿里巴巴开发手册也是强制不允许使用Executors来创建线程池。
Executors工具类无非是把一些特定参数进行了封装,并提供一些方法供我们调用而已,我们并不能灵活地填写参数,策略过于简单,不够友好。
CachedThreadPool和ScheduledThreadPool最大线程数为Integer.MAX_VALUE,如果线程无限地创建,会造成OOM异常。
LinkedBlockingQueue基于链表的FIFO队列,是无界的,默认大小是Integer.MAX_VALUE,因此FixedThreadPool和SingleThreadPool的阻塞队列长度为Integer.MAX_VALUE,如果此时队列被无限地堆积任务,会造成OOM异常。
参考:
https://juejin.cn/post/6844903816513454094