线程池的三个优点:
第一:降低资源消耗。通过重复利用已创建的线程降低线程创建和销毁造成的消耗。
第二:提高响应速度。当任务到达时,任务可以不需要等到线程创建就能立即执行。
第三:提高线程的可管理性。线程是稀缺资源,如果无限制地创建,不仅会消耗系统资源, 还会降低系统的稳定性,使用线程池可以进行统一分配、调优和监控。
一、线程池的构造参数意义
1.线程池基本参数
由ThreadPoolExecutor构造函数可知,构造参数主要包含一下几个参数
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler)
- corePoolSize:核心线程池的大小
- maximumPoolSize:最大线程池的大小
- keepAliveTime:最大线程空闲时间
- unit:时间单位
- workQueue:阻塞队列,当核心线程数无空闲时,将新任务放入阻塞队列
- threadFactory:线程工厂,自定义线程
- handler:拒绝策略,当最大线程数无空闲时,执行拒绝策略
2. 拒绝策略
当线程池到达最大线程数时,会对新加入的任务执行拒绝策略,可以通过实现RejectedExecutionHandler
接口来自定义业务需要的拒绝策略。juc包默认有四种实现策略:
- AbortPolicy:采用该策略时,会直接抛出一个异常。
- CallerRunsPolicy:采用该策略时,会直接调用execute方法来执行被拒绝的任务,除非线程池已关闭。
- DiscardOldestPolicy:采用该策略时,会从阻塞队列中抛弃最老的任务,然后重试execute。
- DiscardPolicy:什么都不做,直接丢弃当前任务。
二、线程池的执行流程
1.线程池的执行
从图中可以看出,当提交一个新任务到线程池时,线程池的处理流程如下:
- 线程池判断核心线程池里的线程是否都在执行任务,如果不是,则创建一个新的工作线程来执行任务,如果核心线程池里的线程都在执行任务,则进入下个流程。
- 线程池判断工作队列是否已经满,如果没有,则将新提交的任务存储在工作队列中,如果队列已满,则进入下个流程。
- 线程池判断当前线程数是否达到最大线程数,如果没有,则创建一个新的工作线程来执行任务,如果达到了,则交给饱和策略来处理任务。
ThreadPoolExecutor执行execute()方法的示意图如下:
2. 任务的提交
可以通过两个方法想线程池提交任务,分别为execute()和submit()方法。
execute()方法用于提交不需要返回指定的任务,所以无法判断任务是否执行成功。
submit()方法用于提交需要返回值的任务。线程池会返回一个future类型的对象,通过这个future对象可以判断任务是否执行成功,并且可以通过future的get()方法来获取返回值,get()方法会阻塞当前线程知道任务完成,而使用get(long timeout, TimeUnit unit)方法则会阻塞当前任务一段时间后立即返回,这时候任务可能没有执行完。
3. 合理配置线程池
根据任务的特性,可以从以下几个角度分析:
- 任务的性质:CPU密集型任务、IO密集型任务和混合型任务。
- 任务的优先级:高、中和低。
- 任务的执行时长:长、中和短。
- 任务的依赖性:是否依赖其它系统资源。
CPU密集型任务应配置尽可能小的 线程,如配置Ncpu+1个线程的线程池。由于IO密集型任务线程并不是一直在执行任务,则应配 置尽可能多的线程,如2*Ncpu。
可以通过 Runtime.getRuntime().availableProcessors()方法获得当前设备的CPU个数。
优先级不同的任务可以使用优先级队列PriorityBlockingQueue来处理。它可以让优先级高 的任务先执行。
执行时间不同的任务可以交给不同规模的线程池来处理,或者可以使用优先级队列,让 执行时间短的任务先执行。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了