线程池原理及使用
一、背景
线程池的作用是管理线程对象,避免重复new Thread消耗资源,控制资源的访问
二、原理
线程池由主线程创建,刚创建时,里面没有一个线程。任务队列是作为参数传进来的。不过,就算队列里面有任务,线程池也不会马上执行它们。
调用 execute() 方法添加一个任务时,线程池会做如下判断:
- 如果正在运行的线程数量小于 corePoolSize,创建线程运行这个task,否则下一步;
- 如果正在运行的线程数量大于或等于 corePoolSize且阻塞队列没有满,把task放入队列,否则下一步。
- 如果在运行的线程数量小于 maximumPoolSize,那么还是要创建线程运行阻塞队列的task,否则下一步;
- 拒绝策略。
执行完成后
- 当一个线程完成任务时,它会从队列中取下一个任务来执行。
- 当一个线程空闲时,超过一定的时间(keepAliveTime)时,且当前运行的线程数大于 corePoolSize,这个线程就被停掉。直到 当前运行的线程数等于corePoolSize 。
三、4种创建方法
创建方法 | 官方解释 | 参数 | |
---|---|---|---|
1 | Executors.newCachedThreadPool() | 无界线程池,具有自动线程回收 | 核心线程数为0,最大线程数为Integer.MAX_VALUE |
2 | Executors.newFixedThreadPool(int) | 固定大小的线程池 | 工作队列无界 |
3 | Executors.newSingleThreadExecutor() | 单个后台线程 | 只有一个核心线程 |
4 | new ThreadPoolExecutor() | 参考7大参数 | 参考7大参数 |
// 使用给定的初始参数创建一个新的ThreadPoolExecutor 。
java.util.concurrent.ThreadPoolExecutor public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
@NotNull TimeUnit unit,
@NotNull BlockingQueue<Runnable> workQueue,
@NotNull ThreadFactory threadFactory,
@NotNull RejectedExecutionHandler handler)
1.corePoolSize
– 要保留在池中的线程数,即使它们处于空闲状态,除非设置了 allowCoreThreadTimeOut
2.maximumPoolSize
– 池中允许的最大线程数
3.keepAliveTime
– 当线程数大于核心数时,这是多余空闲线程在终止前等待新任务的最长时间。
4.unit
– keepAliveTime参数的时间单位
5.workQueue
– 用于在执行任务之前保存任务的队列。 这个队列将只保存execute方法提交的Runnable任务。
6.threadFactory
– 执行程序创建新线程时使用的工厂
7.handler
– 执行被阻塞时使用的处理程序,因为达到了线程边界和队列容量
IllegalArgumentException
– 如果以下情况之一成立: corePoolSize < 0 keepAliveTime < 0 maximumPoolSize <= 0 maximumPoolSize < corePoolSize
四、封装线程池工具类
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Optional;
import java.util.concurrent.*;
/**
* 使用线程池管理线程的工具类
*/
public class ThreadPoolUtils {
private static final Logger logger = LoggerFactory.getLogger(ThreadPoolUtils.class);
private static ExecutorService threadPool = new ThreadPoolExecutor(
Runtime.getRuntime().availableProcessors(),
100,
60L, TimeUnit.SECONDS,
new LinkedBlockingQueue<Runnable>(10000));
/**
* 不需要返回值,不需要等待执行结果的异步任务
*/
public static void execute(Runnable task) {
try {
threadPool.execute(task);
} catch (Exception ex) {
logger.error("ThreadPoolUtils.execute方法异常", ex);
}
}
/**
* 需要返回值,需要等待执行结果的异步任务
*/
public static <T> Optional<Future<T>> submit(Callable<T> task) {
try {
return Optional.of(threadPool.submit(task));
} catch (Exception ex) {
logger.error("ThreadPoolUtils.submit方法异常", ex);
}
return Optional.empty();
}
}
五、测试例子
1.创建任务
创建一个被执行的 Task,实现 Runnable 接口,sleep 模拟任务的执行时间是1s
/**
* 被执行的任务
*/
@Slf4j
class Task implements Runnable {
private final String name;
public Task(String name) {
this.name = name;
}
@Override
@SneakyThrows
public void run() {
log.info("start task " + name);
Thread.sleep(1000);
log.info("end task " + name);
}
}
2.测试单个线程的线程池
/**
* 单个线程的线程池
*/
private void singleThreadPool() {
ExecutorService executorService = Executors.newSingleThreadExecutor();
for (int i = 0; i < 6; i++) {
executorService.submit(new Task("singleThreadPool " + i));
}
executorService.shutdown();
}
输出:
2021-10-14 17:50:25 [INFO] com.zhangjian.learnjava.Client: ------- start the client -------
2021-10-14 17:50:25 [INFO] com.zhangjian.learnjava.Task: start task singleThreadPool 0
2021-10-14 17:50:26 [INFO] com.zhangjian.learnjava.Task: end task singleThreadPool 0
2021-10-14 17:50:26 [INFO] com.zhangjian.learnjava.Task: start task singleThreadPool 1
2021-10-14 17:50:27 [INFO] com.zhangjian.learnjava.Task: end task singleThreadPool 1
2021-10-14 17:50:27 [INFO] com.zhangjian.learnjava.Task: start task singleThreadPool 2
2021-10-14 17:50:28 [INFO] com.zhangjian.learnjava.Task: end task singleThreadPool 2
2021-10-14 17:50:28 [INFO] com.zhangjian.learnjava.Task: start task singleThreadPool 3
2021-10-14 17:50:29 [INFO] com.zhangjian.learnjava.Task: end task singleThreadPool 3
2021-10-14 17:50:29 [INFO] com.zhangjian.learnjava.Task: start task singleThreadPool 4
2021-10-14 17:50:30 [INFO] com.zhangjian.learnjava.Task: end task singleThreadPool 4
2021-10-14 17:50:30 [INFO] com.zhangjian.learnjava.Task: start task singleThreadPool 5
2021-10-14 17:50:31 [INFO] com.zhangjian.learnjava.Task: end task singleThreadPool 5
2021-10-14 17:50:31 [INFO] com.zhangjian.learnjava.Client: ------- stop the client -------
2021-10-14 17:50:31 [INFO] com.zhangjian.learnjava.Client: client shutdown.
测试结果:单个线程的线程池一个任务结束后下一个任务才开始
3.固定大小的线程池
/**
* 固定大小的线程池
*/
private void fixThreadPool() {
ExecutorService executorService = Executors.newFixedThreadPool(4);
for (int i = 0; i < 6; i++) {
executorService.submit(new Task("fixThreadPool " + i));
}
// 使用shutdown()方法关闭线程池的时候,它会等待正在执行的任务先完成,然后再关闭。shutdownNow()会立刻停止正在执行的任务,awaitTermination()则会等待指定的时间让线程池关闭。
executorService.shutdown();
}
输出:
2021-10-14 17:53:56 [INFO] com.zhangjian.learnjava.Client: ------- start the client -------
2021-10-14 17:53:56 [INFO] com.zhangjian.learnjava.Task: start task fixThreadPool 3
2021-10-14 17:53:56 [INFO] com.zhangjian.learnjava.Task: start task fixThreadPool 2
2021-10-14 17:53:56 [INFO] com.zhangjian.learnjava.Task: start task fixThreadPool 1
2021-10-14 17:53:56 [INFO] com.zhangjian.learnjava.Task: start task fixThreadPool 0
2021-10-14 17:53:58 [INFO] com.zhangjian.learnjava.Task: end task fixThreadPool 1
2021-10-14 17:53:58 [INFO] com.zhangjian.learnjava.Task: end task fixThreadPool 0
2021-10-14 17:53:58 [INFO] com.zhangjian.learnjava.Task: end task fixThreadPool 2
2021-10-14 17:53:58 [INFO] com.zhangjian.learnjava.Task: end task fixThreadPool 3
2021-10-14 17:53:58 [INFO] com.zhangjian.learnjava.Task: start task fixThreadPool 5
2021-10-14 17:53:58 [INFO] com.zhangjian.learnjava.Task: start task fixThreadPool 4
2021-10-14 17:53:59 [INFO] com.zhangjian.learnjava.Task: end task fixThreadPool 5
2021-10-14 17:53:59 [INFO] com.zhangjian.learnjava.Task: end task fixThreadPool 4
2021-10-14 17:53:59 [INFO] com.zhangjian.learnjava.Client: ------- stop the client -------
2021-10-14 17:53:59 [INFO] com.zhangjian.learnjava.Client: client shutdown.
测试结果:固定大小的线层池任务释放后后面的线程才能执行
4.无界线程池
/**
* 无界线程池,核心线程数为0,最大线程数为Integer.MAX_VALUE
*/
private void cachedThreadPoll() {
ExecutorService executorService = Executors.newCachedThreadPool();
for (int i = 0; i < 6000; i++) {
executorService.submit(new Task("threadPoolExecutor " + i));
}
executorService.shutdown();
}
测试结果:无界线程池一次会拉起所有的 task
5.自定义线程数量线程池
/**
* 自定义线程数量线程池
*/
private void threadPoolExecutor() {
int core = 2;
int max = 6;
ExecutorService executorService = new ThreadPoolExecutor(core, max, 60L, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>(5));
for (int i = 0; i < 100; i++) {
executorService.submit(new Task("threadPoolExecutor " + i));
}
log.info("executorService shutdown.");
executorService.shutdown();
}
输出:
2021-10-14 19:19:03 [INFO] com.zhangjian.learnjava.Client: ------- start the client -------
2021-10-14 19:19:03 [INFO] com.zhangjian.learnjava.Task: start task threadPoolExecutor 0
2021-10-14 19:19:03 [INFO] com.zhangjian.learnjava.Task: start task threadPoolExecutor 1
2021-10-14 19:19:03 [INFO] com.zhangjian.learnjava.Task: start task threadPoolExecutor 9
2021-10-14 19:19:03 [INFO] com.zhangjian.learnjava.Task: start task threadPoolExecutor 8
2021-10-14 19:19:03 [INFO] com.zhangjian.learnjava.Task: start task threadPoolExecutor 7
2021-10-14 19:19:03 [INFO] com.zhangjian.learnjava.Task: start task threadPoolExecutor 10
Exception in thread "main" java.util.concurrent.RejectedExecutionException: Task java.util.concurrent.FutureTask@1188e820 rejected from java.util.concurrent.ThreadPoolExecutor@2f490758[Running, pool size = 6, active threads = 6, queued tasks = 5, completed tasks = 0]
at java.util.concurrent.ThreadPoolExecutor$AbortPolicy.rejectedExecution(ThreadPoolExecutor.java:2063)
at java.util.concurrent.ThreadPoolExecutor.reject(ThreadPoolExecutor.java:830)
at java.util.concurrent.ThreadPoolExecutor.execute(ThreadPoolExecutor.java:1379)
at java.util.concurrent.AbstractExecutorService.submit(AbstractExecutorService.java:112)
at com.zhangjian.learnjava.Client.threadPoolExecutor(Client.java:66)
at com.zhangjian.learnjava.Client.main(Client.java:27)
2021-10-14 19:19:04 [INFO] com.zhangjian.learnjava.Task: end task threadPoolExecutor 8
2021-10-14 19:19:04 [INFO] com.zhangjian.learnjava.Task: end task threadPoolExecutor 9
2021-10-14 19:19:04 [INFO] com.zhangjian.learnjava.Task: end task threadPoolExecutor 0
2021-10-14 19:19:04 [INFO] com.zhangjian.learnjava.Task: start task threadPoolExecutor 2
2021-10-14 19:19:04 [INFO] com.zhangjian.learnjava.Task: start task threadPoolExecutor 3
2021-10-14 19:19:04 [INFO] com.zhangjian.learnjava.Task: start task threadPoolExecutor 4
2021-10-14 19:19:04 [INFO] com.zhangjian.learnjava.Task: end task threadPoolExecutor 1
2021-10-14 19:19:04 [INFO] com.zhangjian.learnjava.Task: start task threadPoolExecutor 5
2021-10-14 19:19:04 [INFO] com.zhangjian.learnjava.Task: end task threadPoolExecutor 7
2021-10-14 19:19:04 [INFO] com.zhangjian.learnjava.Task: end task threadPoolExecutor 10
2021-10-14 19:19:04 [INFO] com.zhangjian.learnjava.Task: start task threadPoolExecutor 6
2021-10-14 19:19:05 [INFO] com.zhangjian.learnjava.Task: end task threadPoolExecutor 3
2021-10-14 19:19:05 [INFO] com.zhangjian.learnjava.Task: end task threadPoolExecutor 2
2021-10-14 19:19:05 [INFO] com.zhangjian.learnjava.Task: end task threadPoolExecutor 5
2021-10-14 19:19:05 [INFO] com.zhangjian.learnjava.Task: end task threadPoolExecutor 4
2021-10-14 19:19:05 [INFO] com.zhangjian.learnjava.Task: end task threadPoolExecutor 6
测试结果:任务达到最大线程数+阻塞队列数后阻塞不能提交,有拒绝策略决定