线程池是一种管理和复用技术:是为了减少创建和销毁线程的开销,可以限制并发线程数量,防止资源耗尽,统一管理监控线程的状态和执行。
小tips:根据线程池的创建代码可知,当核心线程数没满时,新来的任务会重新创建一个线程来处理,即使前面创建的线程时空闲的;当核心线程数已满但核心线程有空闲时,新任务会被分配给空闲的核心线程执行。
线程销毁时最开始创建的核心线程一定会保留吗?
根据底层代码可知,在线程销毁时,是通过CAS的方式进行线程销毁的,没有区分哪些线程是核心线程哪些不是,也就是说线程销毁是通过竞争的方式来销毁,随机销毁,最后只保留核心线程数个线程就行。
2,异常任务出现时,线程如何处理?
tips:当一个任务出现了异常,代码会将当前执行任务的线程销毁掉,重新再创建一个新的线程?
默认情况下,线程池中的线程会捕获任务中的异常并继续运行。
• 异常传播可能会发生在调用方尝试获取任务结果时。
• 自定义异常处理可以通过线程池的配置或扩展来实现。
• 线程池的健壮性通常会保证线程池不会因为单个任务的异常而崩溃。
3,线程的销毁:
shutdown():把阻塞队列中的任务执行完才会销毁所有线程
shutdownNow():立即销毁所有线程,忽略阻塞队列中的任务。
如果再此时有新任务来,不会再创建线程(代码里有判断)
线程正在拿任务时 如果调用shutdown方法,线程还是会继续拿任务。否则就不拿了。
4,线程创建时机
- JUC中的线程池创建线程是当有任务需要执行时才会创建
- tomcat中的threadPoolExecutor线程池再创建时就会创建好核心线程数的线程数量。
问题:
1,Executors.newFixedThreadPool() 和 Executors.newSingleThreadExecutor() 内部使用的是无界的 LinkedBlockingQueue,使用无界队列可能导致内存溢出
2,Executors.newCachedThreadPool() 创建的线程池没有最大线程数限制,且线程空闲时间超过 60 秒后会被回收。如果任务提交速度过快,线程池会不断创建新线程,导致线程数量过多,从而引发 CPU 调度开销过大 或 系统资源耗尽。
3,Executors 提供的线程池方法隐藏了线程池的实现细节,开发者无法灵活配置参数(如队列大小、线程存活时间等)。
固定大小线程池(Fixed Thread Pool) | 线程数量固定,核心线程数=最大线程数,任务队列满了会直接执行拒绝策略 |
缓存线程池(Cached Thread Pool) | 可以无限增大,如果长时间未使用的线程会被回收,适用于处理大量短生命周期的任务场景。存储任务的队列是SynchronousQueue,队列容量为0,只负责任务传递,效率比较高 |
计划线程池(Scheduled Thread Pool) | 定时/周期性的任务执行,实现方法有三种:1,定时执行一次就结束;2,设置第一次延迟执行时间,且可设置后续固定延迟时间执行任务(周期性);3,任务结束时间后,设置延迟的时间执行下一次任务(周期性) |
单一线程池(Single Thread Executor) | 只有一个线程执行任务,可以保证任务按顺序执行 |
corePoolSize: | 核心线程数量,被创建了就不会被销毁的数量(开始的时候线程为空,当有任务到来时创建,即使其他空闲的基本线程能够执行新任务也会创建线程,等到需要执行的任务数大于线程池基本大小时就不再创建?)。 |
maximumPoolSize: | 最大线程数,线程池允许创建的最大线程数(是在核心线程数创建满了,并且队列中业存满了的情况下才会再次创建线程数量至最大线程数(无界队列除外))。 |
keepAliveTime: | 超出 corePoolSize 后创建的线程存活时间(当超过核心线程数后,又没有线程任务执行,达到该存活时间后,停止该线程)。 |
unit: | 线程活动保持时间的单位,即 keepAliveTime 的时间单位 |
workQueue: | 保存等待执行的任务的队列(阻塞队列),当线程数达到核心线程数且此时有新任务到来就会被放进阻塞队列,如果使用了工作队列 SynchronousQueue(SynchronousQueue 不会存储任何元素),那么当任务数超过线程池的核心线程数时,该任务不会进入队列 |
threadFactory : | 设置创建线程的工厂,决定如何创建线程,可以设置线程的名字、优先级、守护状态 |
handler: | 任务无法执行时的处理器(即拒绝策略)通常包括抛出异常、静默失败、抛弃旧任务等几种策略 |
推荐方案
使用 ThreadPoolExecutor 手动创建线程池
import java.util.concurrent.*;
public class CustomThreadPoolExample {
public static void main(String[] args) {
int corePoolSize = 4; // 核心线程数
int maximumPoolSize = 8; // 最大线程数
long keepAliveTime = 60L; // 空闲线程存活时间
TimeUnit unit = TimeUnit.SECONDS;
BlockingQueue<Runnable> workQueue = new ArrayBlockingQueue<>(100); // 有界队列
ThreadFactory threadFactory = Executors.defaultThreadFactory();
RejectedExecutionHandler handler = new ThreadPoolExecutor.CallerRunsPolicy(); // 拒绝策略
ThreadPoolExecutor executor = new ThreadPoolExecutor(
corePoolSize,
maximumPoolSize,
keepAliveTime,
unit,
workQueue,
threadFactory,
handler
);
// 提交任务
for (int i = 0; i < 200; i++) {
final int taskId = i;
executor.submit(() -> {
System.out.println("任务 " + taskId + " 正在运行,线程:" + Thread.currentThread().getName());
try {
Thread.sleep(1000); // 模拟耗时任务(不会释放锁)
} catch (InterruptedException e) {
e.printStackTrace();
}
});
}
// 关闭线程池
executor.shutdown();
}
}