java中经常需要用到多线程来处理一些业务,有很多人直接在使用多线程的时候直接继承Thread或者实现Runnable接口的方式来创建线程,但这种方式在创建及销毁线程耗费资源、线程上下文切换问题,同时创建过多的线程也可能引发资源耗尽的风险。这个时候引入线程池比较合理,方便线程任务的管理。
本文就线程池的线程创建过程进行demo分析及如何创建线程池给出一些建议。
先看一下Java线程池ThreadPoolExecutor的构造函数参数
public ThreadPoolExecutor(
int corePoolSize, // 核心线程数,即最小线程数
int maximumPoolSize, // 最大线程数,只有当workQueue队列填满时才会创建多于corePoolSize的线程
long keepAliveTime, TimeUnit unit, // 非核心线程的空闲时间超过keepAliveTime就会被自动终止回收,corePoolSize=maxPoolSize时,keepAliveTime参数也就不起作用了(因为不存在非核心线程)
BlockingQueue<Runnable> workQueue, // 任务的排队队列
ThreadFactory threadFactory, // 新线程的产生方式
RejectedExecutionHandler handler) // 拒绝策略,取值有AbortPolicy、CallerRunsPolicy、DiscardOldestPolicy、DiscardPolicy
测试案例:
public class PoolTest {
public static void main(String[] args) throws InterruptedException {
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(2, 5, 60L, TimeUnit.SECONDS,
new ArrayBlockingQueue<>(5), Executors.defaultThreadFactory(), new ThreadPoolExecutor.DiscardPolicy());
for (int i = 0; i < 20; i++) {
threadPoolExecutor.submit(new Runnable() {
@SneakyThrows
@Override
public void run() {
System.out.println(Thread.currentThread().getName() +" 开始执行-----------------"
);
Thread.sleep(10000L);
}
});
System.out.println(Thread.currentThread().getName() +" :::"
+ "--PoolSize:" + threadPoolExecutor.getPoolSize()
+ "--CorePoolSize:" + threadPoolExecutor.getCorePoolSize()
// +"--MaximumPoolSize:" + threadPoolExecutor.getMaximumPoolSize()
+"--TaskCount:" + threadPoolExecutor.getTaskCount()
+"--Queue.size:" + threadPoolExecutor.getQueue().size()
+"--线程池中活动线程数ActiveCount:" + threadPoolExecutor.getActiveCount()
+"--完成线程数CompletedTaskCount:" + threadPoolExecutor.getCompletedTaskCount()
);
Thread.sleep(500L);
}
System.out.println("======================================================");
while (threadPoolExecutor.getActiveCount() >= 0) {
System.out.println(Thread.currentThread().getName()
+ "--PoolSize:" + threadPoolExecutor.getPoolSize()
+ "--CorePoolSize:" + threadPoolExecutor.getCorePoolSize()
+"--MaximumPoolSize:" + threadPoolExecutor.getMaximumPoolSize()
+"--Queue.size:" + threadPoolExecutor.getQueue().size()
+"--线程池中活动线程数ActiveCount:" + threadPoolExecutor.getActiveCount()
+"--完成线程数CompletedTaskCount:" + threadPoolExecutor.getCompletedTaskCount()
);
Thread.sleep(1000L);
}
}
}
运行结果:
pool-1-thread-1 开始执行-----------------
main :::–PoolSize:1–CorePoolSize:2–TaskCount:1–Queue.size:0–线程池中活动线程数ActiveCount:1–完成线程数CompletedTaskCount:0
main :::–PoolSize:2–CorePoolSize:2–TaskCount:2–Queue.size:0–线程池中活动线程数ActiveCount:2–完成线程数CompletedTaskCount:0
pool-1-thread-2 开始执行-----------------
main :::–PoolSize:2–CorePoolSize:2–TaskCount:3–Queue.size:1–线程池中活动线程数ActiveCount:2–完成线程数CompletedTaskCount:0
main :::–PoolSize:2–CorePoolSize:2–TaskCount:4–Queue.size:2–线程池中活动线程数ActiveCount:2–完成线程数CompletedTaskCount:0
main :::–PoolSize:2–CorePoolSize:2–TaskCount:5–Queue.size:3–线程池中活动线程数ActiveCount:2–完成线程数CompletedTaskCount:0
main :::–PoolSize:2–CorePoolSize:2–TaskCount:6–Queue.size:4–线程池中活动线程数ActiveCount:2–完成线程数CompletedTaskCount:0
main :::–PoolSize:2–CorePoolSize:2–TaskCount:7–Queue.size:5–线程池中活动线程数ActiveCount:2–完成线程数CompletedTaskCount:0
main :::–PoolSize:3–CorePoolSize:2–TaskCount:8–Queue.size:5–线程池中活动线程数ActiveCount:3–完成线程数CompletedTaskCount:0
pool-1-thread-3 开始执行-----------------
main :::–PoolSize:4–CorePoolSize:2–TaskCount:9–Queue.size:5–线程池中活动线程数ActiveCount:4–完成线程数CompletedTaskCount:0
pool-1-thread-4 开始执行-----------------
main :::–PoolSize:5–CorePoolSize:2–TaskCount:10–Queue.size:5–线程池中活动线程数ActiveCount:5–完成线程数CompletedTaskCount:0
pool-1-thread-5 开始执行-----------------
main :::–PoolSize:5–CorePoolSize:2–TaskCount:10–Queue.size:5–线程池中活动线程数ActiveCount:5–完成线程数CompletedTaskCount:0
main :::–PoolSize:5–CorePoolSize:2–TaskCount:10–Queue.size:5–线程池中活动线程数ActiveCount:5–完成线程数CompletedTaskCount:0
main :::–PoolSize:5–CorePoolSize:2–TaskCount:10–Queue.size:5–线程池中活动线程数ActiveCount:5–完成线程数CompletedTaskCount:0
main :::–PoolSize:5–CorePoolSize:2–TaskCount:10–Queue.size:5–线程池中活动线程数ActiveCount:5–完成线程数CompletedTaskCount:0
main :::–PoolSize:5–CorePoolSize:2–TaskCount:10–Queue.size:5–线程池中活动线程数ActiveCount:5–完成线程数CompletedTaskCount:0
main :::–PoolSize:5–CorePoolSize:2–TaskCount:10–Queue.size:5–线程池中活动线程数ActiveCount:5–完成线程数CompletedTaskCount:0
main :::–PoolSize:5–CorePoolSize:2–TaskCount:10–Queue.size:5–线程池中活动线程数ActiveCount:5–完成线程数CompletedTaskCount:0
main :::–PoolSize:5–CorePoolSize:2–TaskCount:10–Queue.size:5–线程池中活动线程数ActiveCount:5–完成线程数CompletedTaskCount:0
main :::–PoolSize:5–CorePoolSize:2–TaskCount:10–Queue.size:5–线程池中活动线程数ActiveCount:5–完成线程数CompletedTaskCount:0
main :::–PoolSize:5–CorePoolSize:2–TaskCount:10–Queue.size:5–线程池中活动线程数ActiveCount:5–完成线程数CompletedTaskCount:0
pool-1-thread-1 开始执行-----------------
========================================
main–PoolSize:5–CorePoolSize:2–MaximumPoolSize:5–Queue.size:4–线程池中活动线程数ActiveCount:5–完成线程数CompletedTaskCount:1
pool-1-thread-2 开始执行-----------------
main–PoolSize:5–CorePoolSize:2–MaximumPoolSize:5–Queue.size:3–线程池中活动线程数ActiveCount:5–完成线程数CompletedTaskCount:2
main–PoolSize:5–CorePoolSize:2–MaximumPoolSize:5–Queue.size:3–线程池中活动线程数ActiveCount:5–完成线程数CompletedTaskCount:2
main–PoolSize:5–CorePoolSize:2–MaximumPoolSize:5–Queue.size:3–线程池中活动线程数ActiveCount:5–完成线程数CompletedTaskCount:2
pool-1-thread-3 开始执行-----------------
pool-1-thread-4 开始执行-----------------
main–PoolSize:5–CorePoolSize:2–MaximumPoolSize:5–Queue.size:1–线程池中活动线程数ActiveCount:5–完成线程数CompletedTaskCount:4
pool-1-thread-5 开始执行-----------------
main–PoolSize:5–CorePoolSize:2–MaximumPoolSize:5–Queue.size:0–线程池中活动线程数ActiveCount:5–完成线程数CompletedTaskCount:5
main–PoolSize:5–CorePoolSize:2–MaximumPoolSize:5–Queue.size:0–线程池中活动线程数ActiveCount:5–完成线程数CompletedTaskCount:5
main–PoolSize:5–CorePoolSize:2–MaximumPoolSize:5–Queue.size:0–线程池中活动线程数ActiveCount:5–完成线程数CompletedTaskCount:5
main–PoolSize:5–CorePoolSize:2–MaximumPoolSize:5–Queue.size:0–线程池中活动线程数ActiveCount:5–完成线程数CompletedTaskCount:5
main–PoolSize:5–CorePoolSize:2–MaximumPoolSize:5–Queue.size:0–线程池中活动线程数ActiveCount:5–完成线程数CompletedTaskCount:5
main–PoolSize:5–CorePoolSize:2–MaximumPoolSize:5–Queue.size:0–线程池中活动线程数ActiveCount:4–完成线程数CompletedTaskCount:6
main–PoolSize:5–CorePoolSize:2–MaximumPoolSize:5–Queue.size:0–线程池中活动线程数ActiveCount:3–完成线程数CompletedTaskCount:7
main–PoolSize:5–CorePoolSize:2–MaximumPoolSize:5–Queue.size:0–线程池中活动线程数ActiveCount:3–完成线程数CompletedTaskCount:7
main–PoolSize:5–CorePoolSize:2–MaximumPoolSize:5–Queue.size:0–线程池中活动线程数ActiveCount:3–完成线程数CompletedTaskCount:7
main–PoolSize:5–CorePoolSize:2–MaximumPoolSize:5–Queue.size:0–线程池中活动线程数ActiveCount:1–完成线程数CompletedTaskCount:9
main–PoolSize:5–CorePoolSize:2–MaximumPoolSize:5–Queue.size:0–线程池中活动线程数ActiveCount:0–完成线程数CompletedTaskCount:10
main–PoolSize:5–CorePoolSize:2–MaximumPoolSize:5–Queue.size:0–线程池中活动线程数ActiveCount:0–完成线程数CompletedTaskCount:10
// 下面这行日志是过了60秒之后再从控制台粘贴出来的,说明每隔60秒如果没有新的任务,则线程池会对不活跃的线程进行销毁
main–PoolSize:4–CorePoolSize:2–MaximumPoolSize:5–Queue.size:0–线程池中活动线程数ActiveCount:0–完成线程数CompletedTaskCount:10
线程池创建线程过程结论:
1.线程池收到线程之后判断一下线程池的线程数poolSize是否小于CorePoolSize;如果小于,则新建一个线程,用于执行新提交的任务;
2.如果线程池的线程数poolSize大于CorePoolSize核心线程数,则把新提交的请求放入到ArrayBlockingQueue阻塞队列中;
3.1如果阻塞队列未满,则把新提交的任务加入到阻塞队列;(例如本例子提交5个线程,先执行了前面2个,然后把3-5个任务放到阻塞队列,这时候并不会创建新的线程执行任务)
3.2如果阻塞队列已满,且poolSize小于maximumPoolSize,则线程池新建一个线程用于处理新的任务;(例如本例子提交10个线程,先执行了前面2个,然后再执行第8个,第9,第10个任务;等到线程池的线程有空了再执行第3个,第4个;因为第3-7个任务已经进入了阻塞队列;这时候阻塞队列满了,线程池如果收到新的提交任务,则会执行执行新提交的任务)
3.3 如果阻塞队列已满,且poolSize大于等于maximumPoolSize。这时候新提交过来的线程在线程池中没有线程可以执行新的任务队列中也无法添加,则执行拒绝策略。
Executors创建线程池方法:
方法名 | 功能 |
---|---|
newFixedThreadPool(int nThreads) | 创建固定大小的线程池 |
newSingleThreadExecutor() | 创建只有一个线程的线程池 |
newCachedThreadPool() | 创建一个不限线程数上限的线程池,任何提交的任务都将立即执行 |
查看源码后发现这三个构造方法的底层都是直接用了ThreadPoolExecutor,只不过是不同实现的时候对ThreadPoolExecutor的构造参数做了特殊处理,如下所示。
newFixedThreadPool:
构造方式为new ThreadPoolExecutor(var0, var0, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue())
问题:设置了corePoolSize=maxPoolSize,keepAliveTime=0(此时该参数没作用),无界队列,任务可以无限放入,当请求过多时(任务处理速度跟不上任务提交速度造成请求堆积)可能导致占用过多内存或直接导致OOM异常
newSingleThreadExector:
构造方式为new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue(), var0)
问题:基本同newFixedThreadPool,但是将线程数设置为了1,单线程,弊端和newFixedThreadPool一致
newCachedThreadPool:
构造方式为new ThreadPoolExecutor(0, 2147483647, 60L, TimeUnit.SECONDS, new SynchronousQueue())
问题:corePoolSize=0,maxPoolSize为很大的数,同步移交队列,也就是说不维护常驻线程(核心线程),每次来请求直接创建新线程来处理任务,也不使用队列缓冲,会自动回收多余线程,由于将maxPoolSize设置成Integer.MAX_VALUE,当请求很多时就可能创建过多的线程,导致资源耗尽OOM
结论:
不推荐直接使用Executors创建线程池的4种方法(如newFixedThreadPool),而是直接使用ThreadPoolExecutor来创建线程池,目的就是为了让开发者根据业务实际情况来创建合适的线程池,避免默认线程池的各种各样问题。