线程池的三个优点:

第一:降低资源消耗。通过重复利用已创建的线程降低线程创建和销毁造成的消耗。

第二:提高响应速度。当任务到达时,任务可以不需要等到线程创建就能立即执行。

第三:提高线程的可管理性。线程是稀缺资源,如果无限制地创建,不仅会消耗系统资源, 还会降低系统的稳定性,使用线程池可以进行统一分配、调优和监控。

一、线程池的构造参数意义

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.线程池的执行

image-20220415161437782从图中可以看出,当提交一个新任务到线程池时,线程池的处理流程如下:

  1. 线程池判断核心线程池里的线程是否都在执行任务,如果不是,则创建一个新的工作线程来执行任务,如果核心线程池里的线程都在执行任务,则进入下个流程。
  2. 线程池判断工作队列是否已经满,如果没有,则将新提交的任务存储在工作队列中,如果队列已满,则进入下个流程。
  3. 线程池判断当前线程数是否达到最大线程数,如果没有,则创建一个新的工作线程来执行任务,如果达到了,则交给饱和策略来处理任务。

ThreadPoolExecutor执行execute()方法的示意图如下:

image-20220415161524581

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来处理。它可以让优先级高 的任务先执行。

执行时间不同的任务可以交给不同规模的线程池来处理,或者可以使用优先级队列,让 执行时间短的任务先执行。

posted on   misterD  阅读(59)  评论(0编辑  收藏  举报
编辑推荐:
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
阅读排行:
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了



点击右上角即可分享
微信分享提示