码农知识堂 - 1000bd
  •   Python
  •   PHP
  •   JS/TS
  •   JAVA
  •   C/C++
  •   C#
  •   GO
  •   Kotlin
  •   Swift
  • 《Java并发编程的艺术》读书笔记 - 第九章 - Java中的线程池


    目录

    前言

    线程池的实现原理

    线程池的 7 大参数

    corePoolSize

    maximumPoolSize

    keepAliveTime

    TimeUnit

    BlockingQueue

    ThreadFactory

    RejectedExecutionHandler

    线程池的运行

    线程池的状态

    线程池的使用

    execute()

    submit()

    线程池的关闭

    合理配置线程池

    经验公式


    前言

    Java 中的线程池是运用场景最多的并发框架,几乎所有需要异步或并发执行任务的程序都可以使用线程池。在开发过程中,合理地使用线程池能够带来 3 个好处。

    • 降低资源消耗:通过重复利用已创建的线程降低线程创建和销毁造成的消耗
    • 提高响应速度:当任务到达时,任务可以不需要等到线程创建就能立即执行
    • 提高线程的可管理性:使用线程池可以对线程进行统一分配、调优和监控

    线程池的实现原理

    线程池的 7 大参数

    corePoolSize

    corePoolSize 是核心线程数。当提交一个任务到线程池时,线程池会创建一个线程来执行任务,即使其他空闲的基本线程能够执行新任务,也会创建线程。等到需要执行的任务数大于corePoolSize 时不再创建。

    maximumPoolSize

    线程池允许创建的最大线程数。如果队列满了,并且已创建的线程数小于最大线程数,则线程池会再创建新的线程执行任务。值得注意的是,如果使用了无界的任务队列,这个参数就没什么效果。

    keepAliveTime

    超过核心线程数的线程在不执行任务时的存活时间。

    TimeUnit

    keepAliveTime 的时间单位。

    BlockingQueue

    用于保存等待执行的任务的阻塞队列。常见阻塞队列如下:

    • ArrayBlockingQueue:是一个基于数组结构的有界阻塞队列,此队列按 FIFO(先进先出)原则对元素进行排序。
    • LinkedBlockingQueue:一个基于链表结构的阻塞队列,此队列按 FIFO 排序元素。
    • SynchronousQueue:一个不存储元素的阻塞队列。每个插入操作必须等到另一个线程调用移除操作。
    • PriorityBlockingQueue:一个具有优先级的无限阻塞队列。

    ThreadFactory

    用于设置创建线程的工厂,可以通过线程工厂给每个创建出来的线程设置更有意义的名字。

    RejectedExecutionHandler

    当任务队列和线程池都满的时候采取的任务拒绝策略。

    常见的拒绝策略:

    • AbortPolicy:直接抛出异常
    • CallerRunsPolicy:指定调用者所在线程来运行任务
    • DiscardOldestPolicy:丢弃队列中最近的一个任务,并执行当前任务
    • DiscardPolicy:不处理,直接丢弃

    线程池的运行

    当接收一个任务时,如果当前运行的线程数小于 corePoolSize,则创建新线程来执行任务(这一步骤需要获取全局锁)。如果运行的线程数等于或大于 corePoolSize,则将任务加入BlockingQueue。如果BlockingQueue 容量已满,则创建新的线程来处理任务(需要获取全局锁),若当前线程数大于 maxPoolSize,此时任务将会采用对应的拒绝策略。

    图片来源:百度

    举例:当前线程池参数如下,100 个任务同时执行

    前 10 个任务创建核心线程数执行,11 - 60 个任务加入任务队列,61 - 80 个任务创建新线程执行,剩下 20 个任务直接抛弃。

    线程池的状态

    • RUNNING:运行状态,线程池创建好之后就会进入此状态,如果不手动调用关闭方法,那么线程池在整个程序运行期间都是此状态。
    • SHUTDOWN:关闭状态,不再接受新任务提交,但是会将已保存在任务队列中的任务处理完。
    • STOP:停止状态,不再接受新任务提交,并且会中断当前正在执行的任务、放弃任务队列中已有的任务。
    • TIDYING:整理状态,所有的任务都执行完毕后(也包括任务队列中的任务执行完),当前线程池中的活动线程数降为 0 时的状态。到此状态之后,会调用线程池的 terminated() 方法。
    • TERMINATED:销毁状态,当执行完线程池的 terminated() 方法之后就会变为此状态。

    图片来源:百度

    线程池的使用

    可以使用两个方法向线程池中提交任务,分别为 execute() 和 submit()方法。 

    execute()

    execute() 方法用于提交不需要返回值的任务 

    1. private static void execute() {
    2. poolExecutor.execute(new Runnable() {
    3. @Override
    4. public void run() {
    5. System.out.println("execute...");
    6. }
    7. });
    8. }

    submit()

    submit() 方法用于提交需要返回值的任务 

    1. private static Object submit() throws ExecutionException, InterruptedException {
    2. Future submit = poolExecutor.submit(new Callable() {
    3. @Override
    4. public Object call() throws Exception {
    5. System.out.println("submit...");
    6. return "hello submit";
    7. }
    8. });
    9. return submit.get();
    10. }
    11. 线程池的关闭

      可以通过线程池的 shutdown 或 shutdownNow 方法来关闭线程池。它们的原理是遍历线程池中的工作线程,然后逐个调用线程的 interrupt 方法来中断线程,所以无法响应中断的任务可能永远无法终止。但是它们存在一定的区别,shutdownNow 首先将线程池的状态设置为 STOP 然后尝试停止所有的正在执行或暂停任务的线程,并返回等待执行任务的列表,而 shutdown 只是将线程池的状态设置成 SHUTDOWN 状态,然后中断所有没有正在执行任务的线程。

      合理配置线程池

      要想合理地配置线程池,就必须首先分析任务特性,可以从以下几个角度来分析:

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

        CPU 密集型任务应配置尽可能小的线程,如配置CPU数量 + 1 个线程的线程池。由于 IO密集型任务线程并不是一直在执行任务,则应配置尽可能多的线程,如 2 * CPU数量。可以通过 Runtime.getRuntime().availableProcessors()方法获得设备的CPU个数。

      经验公式

      线程数量 = CPU数量 * CPU期望利用率 * ( 1 + wait time / service time)
      wait time / service time 被称为阻塞系数,CPU 密集型任务的阻塞系数为 0

      • wait time:等待IO完成时间
      • service time:CPU处理任务时间

        例如一个 8 核CPU,希望这部分工作的CPU使用率为 30%,任务等待 IO完成时间为 90ms,任务CPU处理时间为 10ms
        线程数量 = 8 * 50% * (1 + 90 / 10) = 24
    12. 相关阅读:
      ActiveMQ如何处理重复消息?如何保证消息的有序性?如何处理消息堆积?
      Linux--bash、fork、exec、进程替换相关命令
      AttributeError: module ‘tensorflow‘ has no attribute ‘__version__‘
      面试题:Redis和MySQL的事务区别是什么?
      七天.NET 8操作SQLite入门到实战 - 第七天BootstrapBlazor UI组件库引入(1)
      第 45 届国际大学生程序设计竞赛(ICPC)亚洲区域赛(上海),签到题5题
      腾讯持久化框架MMKV原理探究
      Trie树的实现(思路分析)
      并联机器人结构分析与领域应用
      centos7安装mysql5.7
    13. 原文地址:https://blog.csdn.net/shuttlepro/article/details/127822067
      • 最新文章
      • 攻防演习之三天拿下官网站群
        数据安全治理学习——前期安全规划和安全管理体系建设
        企业安全 | 企业内一次钓鱼演练准备过程
        内网渗透测试 | Kerberos协议及其部分攻击手法
        0day的产生 | 不懂代码的"代码审计"
        安装scrcpy-client模块av模块异常,环境问题解决方案
        leetcode hot100【LeetCode 279. 完全平方数】java实现
        OpenWrt下安装Mosquitto
        AnatoMask论文汇总
        【AI日记】24.11.01 LangChain、openai api和github copilot
      • 热门文章
      • 十款代码表白小特效 一个比一个浪漫 赶紧收藏起来吧!!!
        奉劝各位学弟学妹们,该打造你的技术影响力了!
        五年了,我在 CSDN 的两个一百万。
        Java俄罗斯方块,老程序员花了一个周末,连接中学年代!
        面试官都震惊,你这网络基础可以啊!
        你真的会用百度吗?我不信 — 那些不为人知的搜索引擎语法
        心情不好的时候,用 Python 画棵樱花树送给自己吧
        通宵一晚做出来的一款类似CS的第一人称射击游戏Demo!原来做游戏也不是很难,连憨憨学妹都学会了!
        13 万字 C 语言从入门到精通保姆级教程2021 年版
        10行代码集2000张美女图,Python爬虫120例,再上征途
      Copyright © 2022 侵权请联系2656653265@qq.com    京ICP备2022015340号-1
      正则表达式工具 cron表达式工具 密码生成工具

      京公网安备 11010502049817号