首页 > 其他分享 >初识线程池

初识线程池

时间:2023-07-20 15:56:50浏览次数:32  
标签:请求 队列 核心 任务 初识 线程 CPU

内容:    1、什么是线程池

            2、线程池的优点

           3、线程池原理(重点)

           4、理解线程池(重点)

           5、如何设置线程池

 

1、什么是线程池

线程池(英语:thread pool):一种线程使用模式。线程过多会带来调度开销,进而影响缓存局部性和整体性能。而线程池维护着多个线程,等待着监督管理者分配可并发执行的任务。这避免了在处理短时间任务时创建与销毁线程的代价。线程池不仅能够保证内核的充分利用,还能防止过分调度。可用线程数量应该取决于可用的并发处理器、处理器内核、内存、网络sockets等的数量。

  线程的状态:NEW  RUNNABLE  RUNNING  BLOCKED  DEAD

2、线程池的优点

(1)、线程是稀缺资源,使用线程池可以减少创建和销毁线程的次数,每个工作线程都可以重复使用。

(2)、可以根据系统的承受能力,调整线程池中工作线程的数量,防止因为消耗过多内存导致服务器崩溃。

 

3、线程池的实现原理

 

例子: 核心线程数:2

           任务队列大小:3

           最大线程数:5

(1)首次运行线程池:

         同时8个请求: 第1请求和第2请求,  未达到 核心线程数, 直接执行。

                                 第3,4,5个请求,已达到核心线程数, 未达到任务队列大小,直接 进 任务队列。

                                第6,7,8个请求,任务队列也已经满了,未达到最大线程数(还有 3个线程,也就是5减2等于3),则新开线程。

                       所以 所有的请求都会成功处理。

       假如设置线程的存活时间为1分钟,10分钟后,线程池里面只有2个核心线程。

     再次同时8个请求: 第1请求和第2请求  已达到核心线程数,未达到任务队列大小,则进任务队列。

                            第3请求  ,已达到核心线程数,未达到任务队列大小,则进任务队列。

                           第4,5,6请求,任务队列已经满了,未达到最大线程数,则新开线程。 (这种情况是CPU没有及时切换到核心线程的情况)。

                          第7,8请求,只能走拒绝策略了。

                      所以  不一定所有的请求都会成功处理

       但有一种可能会成功处理,那就是入队列的时候,CPU及时切换到核心线程,核心线程立即从队列里面取走。

            比如: 第1请求和第2请求  已达到核心线程数,未达到任务队列大小,则进任务队列。

                       此时CPU切换到核心线程,2个核心线程从队列里面取走了,此时队列为空。

                        第3,4,5个请求,已达到核心线程数, 未达到任务队列大小,直接 进 任务队列。

                       第6,7,8个请求,任务队列也已经满了,未达到最大线程数(还有 3个线程,也就是5减2等于3),则新开线程。

 

4、理解线程池

以java定义的线程池为例:

public ThreadPoolExecutor(
  int corePoolSize,
  int maximumPoolSize,
  long keepAliveTime,
  TimeUnit unit,
  BlockingQueue<Runnable> workQueue,
  ThreadFactory threadFactory,
  RejectedExecutionHandler handler) ;

corePoolSize:指定了线程池中的线程数量,它的数量决定了添加的任务是开辟新的线程去执行,还是放到workQueue任务队列中去;

maximumPoolSize:指定了线程池中的最大线程数量,这个参数会根据你使用的workQueue任务队列的类型,决定线程池会开辟的最大线程数量;

keepAliveTime:当线程池中空闲线程数量超过corePoolSize时,多余的线程会在多长时间内被销毁;

unit:keepAliveTime的单位

workQueue:任务队列,被添加到线程池中,但尚未被执行的任务;它一般分为直接提交队列、有界任务队列、无界任务队列、优先任务队列几种;

              同步阻塞队列:设置为SynchronousQueue队列,SynchronousQueue是一个特殊的BlockingQueue,每执行一个插入操作就会阻塞

             有界的任务队列:有界的任务队列可以使用ArrayBlockingQueue实现

             无界的任务队列:有界任务队列可以使用LinkedBlockingQueue实现

             优先任务队列:优先任务队列通过PriorityBlockingQueue实现

threadFactory:线程工厂,用于创建线程,一般用默认即可;

             自定义ThreadFactory,可以按需要对线程池中创建的线程进行一些特殊的设置,如命名、优先级

handler:拒绝策略;当任务太多来不及处理时,如何拒绝任务;

     AbortPolicy策略:该策略会直接抛出异常,阻止系统正常工作;

     CallerRunsPolicy策略:如果线程池的线程数量达到上限,该策略会把任务队列中的任务放在调用者线程当中运行;

    DiscardOledestPolicy策略:该策略会丢弃任务队列中最老的一个任务,也就是当前任务队列中最先被添加进去的,马上要被执行的那个任务,并尝试再次提交;

    DiscardPolicy策略:该策略会默默丢弃无法处理的任务,不予任何处理。当然使用此策略,业务场景中需允许任务的丢失;

 

5、如何设置线程池

要想合理的配置线程池的大小,首先得分析任务的特性,可以从以下几个角度分析:

  1. 任务的性质:CPU密集型任务、IO密集型任务、混合型任务。
  2. 任务的优先级:高、中、低。
  3. 任务的执行时间:长、中、短。
  4. 任务的依赖性:是否依赖其他系统资源,如数据库连接等。

(1)根据经验值:性质不同的任务可以交给不同规模的线程池执行。

对于不同性质的任务来说,CPU密集型任务应配置尽可能小的线程,如配置CPU个数+1的线程数,IO密集型任务应配置尽可能多的线程,因为IO操作不占用CPU,不要让CPU闲下来,应加大线程数量,如配置两倍CPU个数+1,而对于混合型的任务,如果可以拆分,拆分成IO密集型和CPU密集型分别处理。

若任务对其他系统资源有依赖,如某个任务依赖数据库的连接返回的结果,这时候等待的时间越长,则CPU空闲的时间越长,那么线程数量应设置得越大,才能更好的利用CPU。
当然具体合理线程池值大小,需要结合系统实际情况,在大量的尝试下比较才能得出,以上只是前人总结的规律。

(2)根据理论值:最佳线程数目 = ((线程等待时间+线程CPU时间)/线程CPU时间 )* CPU数目

比如平均每个线程CPU运行时间为0.5s,而线程等待时间(非CPU运行时间,比如IO)为1.5s,CPU核心数为8,那么根据上面这个公式估算得到:((0.5+1.5)/0.5)*8=32。这个公式进一步转化为:

最佳线程数目 = (线程等待时间与线程CPU时间之比 + 1)* CPU数目

可以得出一个结论:
线程等待时间所占比例越高,需要越多线程。线程CPU时间所占比例越高,需要越少线程。
以上公式与之前的CPU和IO密集型任务设置线程数基本吻合。

 

参考:https://www.jianshu.com/p/7726c70cdc40

标签:请求,队列,核心,任务,初识,线程,CPU
From: https://www.cnblogs.com/maohuidong/p/17568617.html

相关文章

  • labwindows 多线程
    https://www.ni.com/zh-cn/support/documentation/supplemental/06/multithreading-in-labwindows--cvi.htmlhttps://forums.ni.com/t5/LabWindows-CVI/DiscardAsyncTimer-returning-before-timer-callback-is-complete/td-p/3943460......
  • linux 8- 线程 守护进程
    linux8day1.终端在unix系统中用户通过终端登录系统得到shell进程,这个终端成为shell进程的控制终端前台进程于后台进程tty可以直接获取终端函数说明:#include<unistd.h>char*ttyname(intfd);功能:由文件描述符查出对应的文件名参数:fd:文件描述符返回值:成功:......
  • java 多线程
    多线程1.进程与线程线程和进程计算机中的两个重要概念,用于管理和执行程序的执行单元1.1进程进程是资源分配的最小单位,通过进程可以创程序的应用。进程是操作系统的一个执行实例,代表了正在运行的程序的一个实例,其有自己的独立的内存空间,包代码、数据、和堆栈等。进程......
  • [记]Rust全局变量+多线程
    staticmutGods:f32=0.0;fnmain(){std::thread::spawn(||loop{unsafe{Gods+=1.0;}esleep::s(1);//延时1s});std::thread::spawn(||loop{unsafe{Gods+=0.1;}e......
  • 多线程:C语言 - 简易线程池的原理和实现
    目录线程循环处理任务,线程不退出1.存储任务--循环读取任务2.函数回调--函数退出,线程不退出本质是事件驱动,是生产者消费者模型1.工作线程--线程池的池化单元,任务的消费者2.线程池持有者--提供多任务,任务的生产者存储任务的数据结构1.任务(task_t)2.任务队列/阻塞队列(task_q......
  • 初识C语言——3
    循环1、while——while(表达式)     循环语句;2、for(表达式1;表达式2;表达式3)      循环语句;for循环语句中三个表达式都可以省略——即判断省略处,恒为真。for(i=2;i=0;i++)//——表达式2,将0赋值给i;0为假,则不满足表达式2;不进入循环。3、do   循环语句;while(......
  • 并发编程 ---- 信号量线程同步
    合集-c#基础(6) 1.编码技巧---如何实现字符串运算表达式的计算07-122.编码技巧---同步锁对象的选定07-133.解读---yield关键字07-174.并发编程---信号量线程同步07-185.并发编程---为何要线程池化07-186.编码技巧---谨防闭包陷阱07-19收起 引言上......
  • 并发编程 ----为何要线程池化
    合集-c#基础(6) 1.编码技巧---如何实现字符串运算表达式的计算07-122.编码技巧---同步锁对象的选定07-133.解读---yield关键字07-174.并发编程---信号量线程同步07-185.并发编程---为何要线程池化07-186.编码技巧---谨防闭包陷阱07-19收起 引言众......
  • 多线程,redis怎么写数据
    项目方案:多线程写数据到Redis介绍在现代应用程序开发中,Redis是一个非常流行的内存数据库,它提供了高性能的键值存储。然而,在高并发场景下,写入大量数据到Redis可能成为性能瓶颈。为了解决这个问题,我们可以使用多线程来提高写入数据到Redis的效率。本文将介绍一个项目方案,通过......
  • 多线程基础
    多线程程序、进程和线程程序就是一系列有序执行的指令集合进程是程序在某个数据集合上的一次运行活动,也是操作系统进行资源分配和保护的基本单位。进程就是程序的一次执行过程,程序是静态的,它作为系统中的一种资源是永远存在的。而进程是动态的,它是动态的产生,变化和消......