线程池详解

线程池的创建方法 总共有 7 种,但总体来说可分为 2 类:

  • 一类是通过 ThreadPoolExecutor 创建的线程池;
  • 另一个类是通过 Executors 创建的线程池。

1. Executors.newFixedThreadPool:创建一个固定大小的线程池,可控制并发的线程数,超出的线程会在队列中等待;

2. Executors.newCachedThreadPool:创建一个可缓存的线程池,若线程数超过处理所需,缓存一段时间后会回收,当执行第二个任务时第一个任务已经完成,

    会复用执行第一个任务的线程,若线程数不够,则一直新建线程;

3. Executors.newSingleThreadExecutor:创建单个线程数的线程池,它可以保证先进先出的执行顺序;

4. Executors.newScheduledThreadPool:创建一个可以执行延迟任务的线程池;

5. Executors.newSingleThreadScheduledExecutor:创建一个单线程的可以执行延迟任务的线程池;

6. Executors.newWorkStealingPool:创建一个抢占式执行的线程池(任务执行顺序不确定),创建一个具有并行级别的work-stealing线程池,

  【JDK 1.8 添加】可以传入线程的数量,不传入,则默认使用当前计算机中可用的cpu数量,能够合理的使用CPU进行对任务操作(并行操作),适合使用在很耗时的任务中。

7. ThreadPoolExecutor:最原始的创建线程池的方式,它包含了 7 个参数可供设置,后面会详细讲。

例如:

   ThreadPoolExecutor threadPool = new ThreadPoolExecutor(5, 50, 100, TimeUnit.SECONDS, new LinkedBlockingQueue<>(1000));

最大线程数:50: 排队队列最大数:1000

当线程池中正在使用的线程数达到了设置的最大的值(50),而且队列已(1000),还有数据进来的话就会报错。

即: 最大排队数 LinkedBlockingQueue要给一个总得要处理的预估数,比如处理100w的数据。

【推荐】线程池不允许使用 Executors 去创建,而是通过 ThreadPoolExecutor 的方式,这样的处理方式让写的同学更加明确线程池的运行规则,规避资源耗尽的风险。

说明:Executors 返回的线程池对象的弊端如下:

1) FixedThreadPool 和 SingleThreadPool:允许的请求队列长度为 Integer.MAX_VALUE,可能会堆积大量的请求,从而导致 OOM。

2)CachedThreadPool:允许的创建线程数量为 Integer.MAX_VALUE,可能会创建大量的线程,从而导致 OOM。

3)也就是说如果执行的job很慢,就尽量不要使用上述2种线程池。相反的,如果处理很快的job,则可以使用。

参数 1:corePoolSize

核心线程数,线程池中始终存活的线程数。

参数 2:maximumPoolSize

最大线程数,线程池中允许的最大线程数,当线程池的任务队列满了之后可以创建的最大线程数。

参数 3:keepAliveTime

最大线程数可以存活的时间,当线程中没有任务执行时,最大线程就会销毁一部分,最终保持核心线程数量的线程。

参数 4:unit:

单位是和参数 3 存活时间配合使用的,合在一起用于设定线程的存活时间

参数 5:workQueue

一个阻塞队列,用来存储线程池等待执行的任务,均为线程安全,它包含以下 7 种类型:

  • ArrayBlockingQueue:一个由数组结构组成的有界阻塞队列。
  • LinkedBlockingQueue:一个由链表结构组成的有界阻塞队列。
  • SynchronousQueue:一个不存储元素的阻塞队列,即直接提交给线程不保持它们。
  • PriorityBlockingQueue:一个支持优先级排序的无界阻塞队列。
  • DelayQueue:一个使用优先级队列实现的无界阻塞队列,只有在延迟期满时才能从中提取元素。
  • LinkedTransferQueue:一个由链表结构组成的无界阻塞队列。与SynchronousQueue类似,还含有非阻塞方法。
  • LinkedBlockingDeque:一个由链表结构组成的双向阻塞队列。

较常用的是 LinkedBlockingQueue 和 Synchronous,线程池的排队策略使用场景:

ArrayBlockingQueue预先分配好一段连续内存,更稳定

LinkedBlockingQueue 读写锁分离,吞吐量更大

参数 6:ThreadFactory

线程工厂,主要用来创建线程,默认为正常优先级、非守护线程。

参数 7:RejectedExecutionHandler

拒绝策略,拒绝处理任务时的策略,系统提供了 4 种可选:

  • AbortPolicy:拒绝并抛出异常。
  • CallerRunsPolicy:使用当前调用的线程来执行此任务。
  • DiscardOldestPolicy:抛弃队列头部(最旧)的一个任务,并执行当前任务。
  • DiscardPolicy:忽略并抛弃当前任务。

默认策略为 AbortPolicy

线程池处理任务流程:

当往线程池中提交新任务时,线程池主要流程如下:
核心线程数 -> 线程队列 -> 最大线程数 -> 拒绝策略

  1. 如果池中任务数 < corePoolSize (核心线程数),创建新线程立即执行任务
  2. 如果池中任务数 > corePoolSize,新任务放到缓存队列当中等待执行
  3. 队列满,线程数量<maxPoolSize,新建线程立即执行任务
  4. 队列满,线程数量>=maxPoolSize,使用拒绝策略拒绝
### Java 线程池详解 #### 创建线程池的方式 Java 提供了几种创建线程池的方法,最常用的是通过 `Executors` 工厂类来获取预配置好的线程池实例。然而,在实际项目中更推荐使用 `ThreadPoolExecutor` 构造器来自定义参数,以获得更好的灵活性和性能控制[^1]。 ```java // 不推荐的做法:固定大小的缓存线程池可能导致资源浪费或耗尽 ExecutorService cachedPool = Executors.newCachedThreadPool(); // 推荐做法:自定义 ThreadPoolExecutor 参数设置 ThreadFactory namedThreadFactory = new ThreadFactoryBuilder().setNameFormat("demo-pool-%d").build(); ThreadPoolExecutor executor = new ThreadPoolExecutor( corePoolSize, maximumPoolSize, keepAliveTime, TimeUnit.SECONDS, workQueue, namedThreadFactory); ``` #### 关键组件说明 - **核心线程数 (`corePoolSize`)** 定义了即使处于空闲状态也会被保留在线程池中的最小线程数量。 - **最大线程数 (`maximumPoolSize`)** 设置允许的最大活动线程数目;当现有任务队列已满而又有新的提交任务时才会触发扩容至该上限。 - **存活时间 (`keepAliveTime`)** 非核心线程闲置后的自动回收等待周期长度。 - **阻塞队列 (`workQueue`)** 存储待处理的任务对象集合,不同类型的队列会影响吞吐量表现及拒绝策略行为。 - **线程工厂 (`threadFactory`)** 负责生产新线程实体,默认实现较为简单,通常建议开发者根据应用场景定制化命名规则以便于调试跟踪。 #### 常见错误与优化技巧 忽视对线程池内部运行状况监控是常见的失误之一。应当定期审查并调整相关参数,确保其适应当前负载需求变化趋势。另外需要注意捕获未预见异常情形下的恢复机制设计,防止因个别失败案例影响整体服务稳定性[^2]。 #### 实践指南 对于Web容器如Tomcat而言,内置有经过优化过的专用线程管理模块用于支撑高并发请求场景下高效运作的要求。尽管如此,了解基础概念仍然有助于更好地理解框架底层运作机理,并能在必要时候做出针对性调优措施。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值