线程池ThreadPoolExecutor底层原理源码分析

这篇具有很好参考价值的文章主要介绍了线程池ThreadPoolExecutor底层原理源码分析。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

线程池执行任务的具体流程是怎样的?

ThreadPoolExecutor中提供了两种执行任务的方法:

  1. void execute(Runnable command)
  2. Future<?> submit(Runnable task)

实际上submit中最终还是调用的execute()方法,只不过会返回⼀个Future对象,用来获取任务执行结果:
execute(Runnable command)方法执行时会分为三步:
线程池ThreadPoolExecutor底层原理源码分析注意:提交⼀个Runnable时,不管当前线程池中的线程是否空闲,只要数量小于核心线程数就会创建新线程。
注意:ThreadPoolExecutor相当于是非公平的,比如队列满了之后提交的Runnable可能会比正在排队的Runnable先执行。

线程池的五种状态是如何流转的?

RUNNING

会接收新任务并且会处理队列中的任务

SHUTDOWN

不会接收新任务并且会处理队列中的任务

STOP

不会接收新任务并且不会处理队列中的任务,并且会中断在处理的任务(注意:⼀个任务能不能被中断得看任务本身)

TIDYING

所有任务都终止了,线程池中也没有线程了,这样线程池的状态就会转为TIDYING,⼀旦达到此状态,就会调用线程池的terminated()

TERMINATED

terminated()执行完之后就会转变为TERMINATED

这五种状态并不能任意转换,只会有以下几种转换情况

  1. RUNNING -> SHUTDOWN:手动调用shutdown()触发,或者线程池对象GC时会调用finalize()从而调用shutdown()
  2. (RUNNING or SHUTDOWN) ->STOP:调用shutdownNow()触发,如果先调shutdown()紧着调 shutdownNow(),就会发生SHUTDOWN-> STOP
  3. SHUTDOWN -> TIDYING:队列为空并且线程池中没有线程时自动转换
  4. STOP -> TIDYING:线程池中没有线程时自动转换(队列中可能还有任务)
  5. TIDYING ->TERMINATED:terminated()执行完后就会自动转换

线程池中的线程是如何关闭的?

Thread类提供了⼀个stop(),但是标记了@Deprecated,为什么不推荐用stop()方法来停掉线程呢?
因为stop()方法太粗暴了,⼀旦调用了stop(),就会直接停掉线程,但是调用的时候根本不知道线程刚刚在做什么,任务做到哪⼀步了,这是很危险的。
这⾥强调⼀点,stop()会释放线程占用的synchronized锁(不会自动释放ReentrantLock锁,这也是不建议用stop()的⼀个因素)。
所以,我们建议通过自定义⼀个变量,或者通过中断来停掉⼀个线程

线程池为什么⼀定得是阻塞队列?

而线程池中的线程在运行过程中,执行完创建线程时绑定的第⼀个任务后,就会不断的从队列中获取任务并执行,那么如果队列中没有任务了,线程为了不自然消亡,就会阻塞在获取队列任务时,等着队列中有任务过来就会拿到任务从e去执行任务。
通过这种方法能最终确保,线程池中能保留指定个数的核心线程数
线程池ThreadPoolExecutor底层原理源码分析

某个线程在从队列获取任务时,会判断是否使用超时阻塞获取,我们可以认为非核心线程会poll(),核心线程会take(),非核心线程超过时间还没获取到任务后面就会自然消亡了。

线程发生异常,会被移出线程池吗?

答案是会的,那有没有可能核心线程数在执行任务时都出错了,导致所有核心线程都被移出了线程池?
线程池ThreadPoolExecutor底层原理源码分析在源码中,当执行任务时出现异常时,最终会执行processWorkerExit(),执行完这个方法后,当前线程也就自然消亡了,但是!processWorkerExit()方法中会额外再新增⼀个线程,这样就能维持住固定的核心线程数。

Tomcat是如何自定义线程池的?

Tomcat中用的线程池为org.apache.tomcat.util.threads.ThreadPoolExecutor,注意类名和JUC下的⼀样,但是包名不⼀样。
Tomcat会创建这个线程池:
线程池ThreadPoolExecutor底层原理源码分析
注入传入的队列为TaskQueue,它的入队逻辑为:
线程池ThreadPoolExecutor底层原理源码分析
特殊在:

  • 入队时,如果线程池的线程个数等于最大线程池数才入队
  • 入队时,如果线程池的线程个数小于最大线程池数,会返回false,表示入队失败

这样就控制了,Tomcat的这个线程池,在提交任务时:

  1. 仍然会先判断线程个数是否小于核心线程数,如果小于则创建线程
  2. 如果等于核心线程数,会入队,但是线程个数小于最大线程数会入队失败,从而会去创建线程

所以随着任务的提交,会优先创建线程,直到线程个数等于最大线程数才会入队。
当然其中有⼀个比较细的逻辑是:在提交任务时,如果正在处理的任务数小于线程池中的线程个数,那么也会直接入队,而不会去创建线程,也就是上面源码中getSubmittedCount的作用。

线程池的核心线程数、最大线程数该如何设置?我们都知道,线程池中有两个非常重要的参数:

  1. corePoolSize:核心线程数,表示线程池中的常驻线程的个数
  2. maximumPoolSize:最大线程数,表示线程池中能开辟的最大线程个数

那这两个参数该如何设置呢?
我们对线程池负责执行的任务分为三种情况:

  1. CPU密集型任务,比如找出1-1000000中的素数
  2. IO密集型任务,比如文件IO、⽹络IO
  3. 混合型任务

CPU密集型任务的特点时,线程在执行任务时会⼀直利用CPU,所以对于这种情况,就尽可能避免发生线程上下文切换。
线程在执行IO型任务时,可能大部分时间都阻塞在IO上,假如现在有10个CPU,如果我们只设置了10个线程来执行IO型任务,那么很有可能这10个线程都阻塞在了IO上,这样这10个CPU就都没活⼲了,所以,对于IO型任务,我们通常会设置线程数为:2*CPU核心数

总结,我们在工作中,对于:文章来源地址https://www.toymoban.com/news/detail-456187.html

  1. CPU密集型任务:CPU核心数+1,这样既能充分利用CPU,也不⾄于有太多的上下文切换成本
  2. IO型任务:建议压测,或者先用公式计算出⼀个理论值(理论值通常都比较小)
  3. 对于核心业务(访问频率高),可以把核心线程数设置为我们压测出来的结果,最大线程数可以等
    于核心线程数,或者大⼀点点,比如我们压测时可能会发现500个线程最佳,但是600个线程时也还行,此时600就可以为最大线程数
  4. 对于非核心业务(访问频率不高),核心线程数可以比较小,避免操作系统去维护不必要的线程,最大线程数可以设置为我们计算或压测出来的结果

到了这里,关于线程池ThreadPoolExecutor底层原理源码分析的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Go语言网络编程:HTTP服务端之底层原理与源码分析——http.HandleFunc()、http.ListenAndServe()

    在 Golang只需要几行代码便能启动一个 http 服务,在上述代码中,完成了两件事: 调用 http.HandleFunc 方法,注册了对应于请求路径 /ping 的 handler 函数 调用 http.ListenAndServe,启动了一个端口为 8999 的 http 服务 2.1 server 结构 Addr :表示服务器监听的地址。如\\\":8080\\\"表示服务器在本地

    2024年02月08日
    浏览(58)
  • 面试题:线程池的底层工作原理

    线程池的几个重要的参数: 1、corePoolSize:线程池的核心线程数(也是默认线程数) 2、maximumPoolSize:最大线程数 3、keepAliveTime:允许的线程最大空闲时间(单位/秒) 线程池内部是通过队列+线程实现的,当我们利用线程池执行任务时: 如果此时线程池中的线程数量小于core

    2024年02月12日
    浏览(55)
  • 微信小程序底层框架实现原理:开篇-双线程架构

    介绍章节我们聊了一下“小程序”的一些概念和发展历程,并且拓展了一下思路。我们从本章节开始讲解小程序的架构。 本章节分解如下: 小程序的双线程架构设计。 双线程对比单线程的优势在哪里。 传统h5开发环境有什么弊端。 Native层在双线程架构中起到怎样的作用。

    2024年03月23日
    浏览(45)
  • 线程中并发安全问题(Sychronized关键字的底层原理)

    Sychronized的底层原理 ​ sychronized 对象锁采用互斥方式让同一时刻至多只有一个线程能持有对象锁,其他线程想获取这个对象锁只能被阻塞。 Monitor Sychronized的底层实现Monitor。 WaitSet:关联调用了wait方法的线程,用于存储处于等待状态的线程。 EntryList:关联了没有获得

    2024年02月16日
    浏览(37)
  • JUC编程之——线程的start方法及底层源码

    简单的写一个线程的小栗子: 查看java start()方法的源码: 更加底层的C++代码: thread.c (代码位置:openJDKsrcsharenativejavalang) jvm.cpp (代码位置:openIDKhotspotsrcsharevmprims) thread.cpp (代码位置:openJDKhotspotsrcsharevmruntime) 代码——OpenJDK源码网址:http://openjdk.java.net/ 2.3.1 thre

    2023年04月13日
    浏览(39)
  • java八股文面试[多线程]——Synchronized的底层实现原理

    笔试:画出Synchronized 线程状态流转 实现原理图 synchronized解决的是多个线程之间访问资源的同步性,synchronized 翻译为中文的意思是 同步 ,也称之为”同步锁“。 synchronized的作用是保证在 同一时刻 , 被修饰的代码块或方法只会有一个线程执行,以达到保证并发安全的

    2024年02月10日
    浏览(46)
  • java八股文面试[多线程]——ThreadLocal底层原理和使用场景

    源码分析: ThreadLocal中定义了ThreadLocalMap静态内部类,该内部类中又定义了Entry内部类。 ThreadLocalMap定了 Entry数组。 Set方法: Get方法: Thread中定义了两个ThreaLocalMap成员变量: Spring使用ThreadLocal解决线程安全问题  我们知道在一般情况下,只有 无状态的Bean 才可以在多线程环

    2024年02月10日
    浏览(49)
  • 自定义线程池 ThreadPoolExecutor

    Executor框架最核心的类是ThreadPoolExecutor,它是线程池的实现类,主要由下列4个组件构成。 ·corePool:核心线程池的大小。 ·maximumPool:最大线程池的大小。 ·BlockingQueue:用来暂时保存任务的工作队列。 ·RejectedExecutionHandler:当ThreadPoolExecutor已经关闭或ThreadPoolExecutor已经饱和时

    2024年02月05日
    浏览(33)
  • 进程与线程、线程创建、线程周期、多线程安全和线程池(ThreadPoolExecutor)

    什么进程? 进程是 资源(CPU、内存等)分配 的基本单位,它是程序执行时的一个实例。程序运行时系统就会创建一个进程,并为它分配资源,然后把该进程放入进程就绪队列,进程调度器选中它的时候就会为它分配CPU时间,程序开始真正运行。 什么是线程? 线程是操作系

    2024年02月14日
    浏览(39)
  • java线程池ThreadPoolExecutor使用

    在开发服务端软件项目时,软件经常需要处理执行时间很短而数目巨大的请求,如果为每一个请求创建一个新的线程,则会导致性能上的瓶颈。因为JVM需要频繁的处理线程对象的创建与销毁,如果请求的执行时间很短,则有可能花在创建和销毁线程对象上的时间大于真正执行

    2024年02月06日
    浏览(52)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包