为啥要用线程池?线程池工作原理?创建线程池 | 大别山码将

@为啥要用线程池,线程池的参数含义

创建线程和销毁线程的花销是比较大的,频繁的创建线程和销毁线程,可能导致系统资源不足。使用线程池我们可以把创建和销毁的线程的过程去掉

使⽤线程池的好处

  • 降低资源消耗。通过重复利⽤已创建的线程降低线程创建和销毁造成的消耗。
  • 提⾼响应速度。当任务到达时,任务可以不需要等到线程创建就能⽴即执⾏。
  • 提⾼线程的可管理性。线程是稀缺资源,如果⽆限制的创建,不仅会消耗系统资源,还会降低系统的稳定性,使⽤线程池可以进⾏统⼀的分配,调优和监控。

线程池创建需要的几个核心参数:

  • corePoolSize:线程池中的核心线程数

  • maximumPoolSize:线程池中最大线程数

  • keepAliveTime:闲置超时时间

  • unit:keepAliveTime 超时时间的单位(时/分/秒等)

  • workQueue:线程池中的任务队列

  • threadFactory:为线程池提供创建新线程的线程工厂

  • rejectedExecutionHandler:线程池任务队列超过最大值之后的拒绝策略

线程池原理:

一个线程池管理了一组工作线程同时它还包括了一个用于放置等待执行 任务的任务队列(阻塞队列)。
     默认情况下,在创建了线程池后,线程池中的线程数为 0。当任务提交给 线程池之后的处理策略如下:

      1. 如果此时线程池中的线程数量**小于 corePoolSize(核心线程数(或核心池的大小))**,既使线程池中的线程都处于空闲状态,也要创建新的线程来处理被添加的任务(**也

就是每来一个任务,就要创建一个线程来执行任务**)。
     2. 如果此时线程池中的线程数量大于等于 核心线程数,但是缓冲队列 workQueue 未满***,那么任务会被放入缓冲队列,则该任务会等待空闲线程将其 取出去执行
     3. 如果此时线程池中的数量
大于等于 核心线程数
***, 缓冲队列 workQueue 满,并且线程池中的数量小于 maximumPoolSize(线程池 最大线程数),建新的线程来处理被添加的任务。
     4. 如果此时线程池中的数量
大于等于 核心线程数
*, 缓冲队列 workQueue 满,并且线程池中的数量等于最大线程数,那么通 过任务拒绝策略来处理此任务。 也就是处理任务的优先级为: 核 心 线 程
corePoolSize 、 任 务 队 列 workQueue、最大线程 maximumPoolSize,如果三者都满了,使用
handler 处理被拒绝的任务。
     5. 特别注意,在 核心线程数 和 最大线程数 之间的线程 数会被自动释放。当线程池中线程数量
大于 核心线程数时
,如果某线程 空闲时间超过 keepAliveTime,线程将被终止,直至线程池中的线程数目不 大于
核心线程数。这样,线程池可以动态的调整池中的线程数。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-wrxqVGfl-1632750345572)(C:\Users\LENOVO-LX\AppData\Roaming\Typora\typora-user-images\image-20210914202500236.png)]

当线程池中有任务需要执行时,线程池会判断如果线程数量没有超过核心数量就会新建线程池进行任务执行,如果线程池中的线程数量已经超过核心线程数,这时候任务就会被放入任务队列中排队等待执行;如果任务队列超过最大队列数,并且线程池没有达到最大线程数,就会新建线程来执行任务;如果超过了最大线程数,就会执行拒绝执行策略。

我们在代码中模拟了 10 个任务,我们配置的核⼼线程数为 5 、等待队列容量为 100 ,所以每次只可能存在 5 个任务同时执⾏,剩下的 5 个任务会被放到等待队列中去。当前的 5 个任务之⾏完成后,才会之⾏剩下的 5 个任务。

简单线程池的创建:

⽅式⼀:通过构造⽅法实现

⽅式⼆:通过 Executor 框架的⼯具类 Executors 来实现

我们可以创建三种类型的 ThreadPoolExecutor:

  • FixedThreadPool该⽅法返回⼀个固定线程数量的线程池。该线程池中的线程数量始终不变。当有⼀个新的任务提交时,线程池中若有空闲线程,则⽴即执⾏。若没有,则新的任务会被暂存在⼀个任务队列中,待有线程空闲时,便处理在任务队列中的任务。
  • SingleThreadExecutor**:** ⽅法返回⼀个只有⼀个线程的线程池。若多余⼀个任务被提交到该线程池,任务会被保存在⼀个任务队列中,待线程空闲,按先⼊先出的顺序执⾏队列中的任务。
  • CachedThreadPool**:** 该⽅法返回⼀个可根据实际情况调整线程数量的线程池。线程池的线程数量不确定,但若有空闲线程可以复⽤,则会优先使⽤可复⽤的线程。若所有线程均在⼯作,⼜有新的任务提交,则会创建新的线程处理任务。所有线程在当前任务执⾏完毕后,将返回线程池进⾏复⽤。

一个典型的线程池,应该包括如下几个部分:
1、线程池管理器(ThreadPool),用于启动、停用,管理线程池
2、工作线程(WorkThread),线程池中的线程
3、请求接口(WorkRequest),创建请求对象,以供工作线程调度任务的执行
4、请求队列(RequestQueue),用于存放和提取请求
5、结果队列(ResultQueue),用于存储请求执行后返回的结果

线程池管理器向请求队列(RequestQueue) 添加请求,这些请求事先需要实现请求接口,即传递工作函数、参数、结果处理函数、以 及异常处理函数。之后初始化一定数量的工作线程,这些线程通过轮询的方式不断查看请 求队列(RequestQueue)有没有请求存在,有则会提取出请求,进行执行。然后,线程池 管理器调用方法(poll)查看结果队列(resultQueue)是否有值,如果有值,则取出,调 用结果处理函数执行。

因此,对这个队列的设计,要实现线程同步,以及一定阻塞和超时机制的设计, 以防止因为不断轮询而导致的过多 cpu 开销。

如何合理的配置 java 线程池?

1)配置线程池时,CPU 密集型任务可以少配置线程数,大概和机器的 cpu 核数相 当,可以使得每个线程都在执行任务。
     2)IO 密集型任务则由于需要等待 IO 操作,线程并不是一直在执行任务,则配置尽 可能多的线程数,大概是2倍的 cpu 核数。
     3)有界队列和无界队列的配置需区分业务场景,一般情况下配置有界队列。。在一些 可能会有爆发性增长的情况下使用无界队列。
     4)任务非常多时,使用非阻塞队列,使用 CAS 操作替代锁可以获得好的吞吐量(减少了上下文切换)。 synchronousQueue 吞吐率最高。

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

李莲花*

多谢多谢,来自一名大学生的感谢

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值