Java线程池让使用线程变得更加高效

使用一个线程需要经过创建、运行、销毁三大步骤,如果业务系统每个线程都要经历这个过程,那势必带来过多不必要的资源消耗。线程池就是为了解决这个问题而生,需要时就从池中拿取,使用完毕就放回去,池化思想通过复用对象大大提高了系统的性能。线程池、数据库连接池、对象池等都采用了池化技术,下面我们就来学习下线程池的核心知识、面试重点~

文章目录

    • 1. 线程池使用
      • 1.1 如何配置线程池大小
      • 1.2 创建线程池
      • 1.3 预配置线程池弊端
      • 1.3 Spring创建线程池
    • 2. 线程池拒绝策略
    • 3. 线程工厂的作用
    • 未完待续。。。

1. 线程池使用

1.1 如何配置线程池大小

面试官:你说下线程池的大小要怎么配置?

这个问题要看业务系统执行的任务更多的是计算密集型任务,还是I/O密集型任务。大家可以从这两个方面来回答面试官。

(1)如果是计算密集型任务,通常情况下,CPU个数为N,设置N + 1个线程数量能够实现最优的资源利用率。因为N + 1个线程能保证至少有N个线程在利用CPU,提高了CPU利用率;同时不设置过多的线程也能减少线程状态切换所带来的上下文切换消耗。

(2)如果是I/O密集型任务,线程的主要等待时间是花在等待I/O操作上,另外就是计算所花费的时间。一般可以根据这个公式得出线程池合适的大小配置。
线程池大小 = C P U 数量 ∗ C P U 期望的利用率 ∗ ( 1 + I O 操作等待时间 / C P U 计算时间 ) 线程池大小 = CPU数量 * CPU期望的利用率 * (1 + IO操作等待时间/CPU计算时间) 线程池大小=CPU数量CPU期望的利用率(1+IO操作等待时间/CPU计算时间)

1.2 创建线程池

面试官:那线程池怎么创建?

可以使用ThreadPoolExecutor自定义创建线程池,这也是创建线程池推荐的创建方式。

public ThreadPoolExecutor(int corePoolSize, // 要保留在池中的线程数
                          int maximumPoolSize, // 池中允许的最大线程数
                          long keepAliveTime, // 当线程数大于corePoolSize时,多余的空闲线程在终止之前等待新任务的最长时间
                          TimeUnit unit, // 时间单位
                          BlockingQueue<Runnable> workQueue, // 在执行任务之前用于保存任务的队列
                          ThreadFactory threadFactory) { // 执行程序创建新线程时使用的工厂
    this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
         threadFactory, defaultHandler);
}

另外Executors类也提供了一些静态工厂方法,可以用来创建一些预配置的线程池。

newFixedThreadPool可以设置线程池的固定线程数量。

public static ExecutorService newFixedThreadPool(int nThreads) {
    return new ThreadPoolExecutor(nThreads, nThreads,
                                  0L, TimeUnit.MILLISECONDS,
                                  new LinkedBlockingQueue<Runnable>());
}

newSingleThreadExecutor可以让线程按序执行,适用于需要确保所有任务按序执行的场景。

public static ExecutorService newSingleThreadExecutor() {
    return new FinalizableDelegatedExecutorService
        (new ThreadPoolExecutor(1, 1,
                                0L, TimeUnit.MILLISECONDS,
                                new LinkedBlockingQueue<Runnable>()));
}

大家看下以下源码,newCachedThreadPool的线程数没有上限限制,同时空闲线程的存活时间是60秒。newCachedThreadPool更适合系统负载不太高、线程执行时间短的场景下,因为线程任务不需要经过排队,直接交给空闲线程就可以。

public static ExecutorService newCachedThreadPool() {
    return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                  60L, TimeUnit.SECONDS,
                                  new SynchronousQueue<Runnable>());
}

newScheduledThreadPool可以安排任务在给定的延迟后运行,或者定期执行。

public static ScheduledExecutorService newScheduledThreadPool(
        int corePoolSize, ThreadFactory threadFactory) {
    return new ScheduledThreadPoolExecutor(corePoolSize, threadFactory);
}

1.3 预配置线程池弊端

面试官:你说的这些预配置线程池会有什么问题?

小伙伴要记得上述静态工厂方法在使用过程中可能会出现OOM内存溢出的情况。

  1. newFixedThreadPoolnewSingleThreadExecutor:因为线程池指定的请求队列类型是链表队列LinkedBlockingQueue<Runnable>(),故允许的请求队列长度是无上限的,可能会出现OOM内存溢出。
  2. newCachedThreadPoolnewScheduledThreadPool:线程池指定的线程数上限是Integer.MAX_VALUE,故允许创建的线程数量是无上限的Integer.MAX_VALUE,可能会出现OOM内存溢出。

1.3 Spring创建线程池

面试官:你们项目线程池用的这种创建方式?

一般Spring工程创建线程池不直接使用ThreadPoolExecutor。

Spring框架提供了以Bean形式来配置线程池的ThreadPoolTaskExecutor类,ThreadPoolExecutor类的底层实现还是基于JDK的ThreadPoolExecutor。

# 示例代码
@Bean(name = "testExecutor")
public ThreadPoolTaskExecutor testExecutor() {
    ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
    // 配置核心线程数
    executor.setCorePoolSize();
    // 配置最大线程数
    executor.setMaxPoolSize();
    // 配置队列大小
    executor.setQueueCapacity();
    executor.initialize();
    return executor;
}

2. 线程池拒绝策略

面试官:线程池请求队列满了,有新的请求进来怎么办?

大家如果有看ThreadPoolExecutor源码就知道,ThreadPoolExecutor类实现了setRejectedExecutionHandler方法,顾名思义意思是设置拒绝执行处理程序。

# ThreadPoolExecutor源码
/**
* Sets a new handler for unexecutable tasks. // 为无法执行的任务设置新的处理程序
*
* @param handler the new handler
* @throws NullPointerException if handler is null
* @see #getRejectedExecutionHandler
*/
public void setRejectedExecutionHandler(RejectedExecutionHandler handler) {
    if (handler == null)
        throw new NullPointerException();
    this.handler = handler;
}

该方法可以为线程池设置拒绝策略,目前JDK8一共有四种拒绝策略,也对应入参RejectedExecutionHandler的四种子类实现。

在这里插入图片描述

  1. AbortPolicy:默认的拒绝策略,直接抛出RejectedExecutionException异常。
  2. CallerRunsPolicy:直接在execute方法的调用线程中运行被拒绝的任务。
  3. DiscardPolicy:直接丢弃被拒绝的任务。
  4. DiscardOldestPolicy:丢弃最旧的未处理请求,然后重试execute 。

另外如果线程池拒绝策略设置为DiscardOldestPolicy,线程池的请求队列类型最好不要设置为优先级队列PriorityBlockingQueue。因为该拒绝策略是丢弃最旧的请求,也就意味着丢弃优先级最高的请求

3. 线程工厂的作用

面试官:线程池的入参ThreadFactory有什么作用吗?

ThreadFactory定义了创建线程的工厂,回答这个问题我们就要结合实际场景了。

ThreadFactory线程工厂能够为线程池里每个线程设置名称、同时设置自定义异常的处理逻辑,可以方便我们通过日志来定位bug的位置。

以下是一个代码示例。

@Slf4j
public class CustomGlobalException {
    public static void main(String[] args) {
        ThreadFactory factory = r -> {
            String threadName = "线程名称";
            Thread thread = new Thread(r, threadName);
            thread.setUncaughtExceptionHandler((t, e) -> {
                log.error("{}执行了自定义异常日志", threadName);
            });
            return thread;
        };
        ExecutorService executor = new ThreadPoolExecutor(6,
                6,
                0,
                TimeUnit.SECONDS,
                new ArrayBlockingQueue<>(66),
                factory);

        executor.execute(() -> {
            throw new NullPointerException();
        });
        executor.shutdown();
    }
}

控制台打印:2024-04-26 22:04:45[ ERROR ]线程名称执行了自定义异常日志

🌱以【面试官面试】形式覆盖Java程序员所需掌握的Java核心知识、面试重点,本博客收录在我开源的《Java学习指南》中,会一直完善下去,希望收到大家的 ⭐ Star ⭐支持,这是我创作的最大动力: https://github.com/hdgaadd/JavaGetOffer

未完待续。。。

创作不易,不妨点赞、收藏、关注支持一下,各位的支持就是我创作的最大动力❤️

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/579210.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

SpringBoot---------Hutool

第一步&#xff1a;引入依赖 <dependency><groupId>cn.hutool</groupId><artifactId>hutool-parent</artifactId><version>5.7.17</version></dependency> 第二步&#xff1a;各种用法 ①生成随机数 //生成验证码 String s …

游戏新手村21:再谈游戏广告页面设计

前文我们说到了网页游戏的LandingPage页面设计中需要遵循的一些规范和注意事项&#xff0c;本章我们重点谈下网络游戏的广告页面设计。 之前在金山的时候&#xff0c;大家习惯或者喜欢称LandingPage为分流页&#xff0c;这个页面需要加入哪些游戏信息才能在短时间内俘获玩家的…

cJSON的使用

文章目录 一、CJSON初识二、CJSON解析器基础三、CJSON解析数据JSON解析基础CJSON解析数组数据CJSON解析嵌套数据 五、创建JSON数据 一、CJSON初识 JSON (JavaScript Object Notation)是一种轻量级的数据交换格式&#xff0c;常用于在网络之间传输数据。它是一种文本格式&#…

Linux计划任务书以及定时任务的编写

一、程序可以通过两种方式执行&#xff1a; 手动执行利用调度任务&#xff0c;依据一定的条件自动执行 自动执行可通过一下两个命令来实现: &#xff08;1&#xff09;At &#xff08;单一工作调度&#xff09; &#xff08;2&#xff09;Cron &#xff08;循环工作调度&a…

微信小程序的开发

1.了解项目的基本组成结构 pages 用来存放所有小程序的页面 utils 用来存放工具性质的模块(例如:格式化时间的自定义模块) app.js 小程序项目的入口文件 app.json 小程序项目的全局配置文件 app.wxss 小程序项目的全局样式文件 project.config.json 项目的配置文件 sitem…

(GEE)2000-2020年黄河流域时序渐变图及高程模型计算 JavaScript版

文章目录 一. 选取目标区域二. NDVI实现三. 高程模型DEM实现四. 时序图五. 植被覆盖类型六. 参考文献 首先推荐吴秋生老师团队开源的便捷构建网站&#xff1a;适用于地理空间应用的Streamlight 吴秋生老师团队的工具请自行探索。本文讲解基于GEE云开发平台实现&#xff0c;基于…

吾日三省吾身---对平常遇到的错误总结

✨个人主页&#xff1a; 不漫游-CSDN博客 前言 本篇文章是对平常练习遇到的问题总结&#xff0c;多吸取经验教训才能避免未来再犯~ Java语法部分 &#xff08;一&#xff09;多态 思考&#xff1a;这道题很明显考察的是多态的知识点&#xff0c;即一个对象可以被赋值给其父类…

11.盛最多水的容器 C++

一开始我最先想到的是暴力解法&#xff0c;就是两个循环嵌套依次遍历&#xff0c;所有情况都过一遍找出最大值&#xff0c;这样示例的结果虽然是正确的&#xff0c;但是超时。所以暴力解法行不通&#xff0c;双指针思考才是正道&#xff0c;双指针一般都是一边一个&#xff0c;…

拉链法解决哈希冲突

1.基本思想: 相同散列地址的记录链成一单链表,m个散列地址就设m个单链表,然后用一个数组将m个单链表的表头指针存储起来,形成一个动态的结构. 例如:一组关键字为{19,14,23,1,68,20,84,27,55,11,10,79},散列函数为:Hash(key)key%13, 就会发现有些元素是同义词,比如14%131,1%131…

江开2024年春《计算机组成原理 060214》第4次计分作业参考答案

答案&#xff1a;更多答案&#xff0c;请关注【电大搜题】微信公众号 答案&#xff1a;更多答案&#xff0c;请关注【电大搜题】微信公众号 答案&#xff1a;更多答案&#xff0c;请关注【电大搜题】微信公众号 单选题 1某计算机字长32位&#xff0c;其存储容量为4GB&am…

MySQL__锁

文章目录 &#x1f60a; 作者&#xff1a;Lion J &#x1f496; 主页&#xff1a; https://blog.csdn.net/weixin_69252724 &#x1f389; 主题&#xff1a; MySQL__锁&#xff09; ⏱️ 创作时间&#xff1a;2024年04月27日 ———————————————— 这里写目录…

Java高阶私房菜-JVM垃圾回收机制及算法原理探究

目录 垃圾回收机制 什么是垃圾回收机制 JVM的自动垃圾回收机制 垃圾回收机制的关键知识点 初步了解判断方法-引用计数法 GCRoot和可达性分析算法 什么是可达性分析算法 什么是GC Root 对象回收的关键知识点 标记对象可回收就一定会被回收吗&#xff1f; 可达性分析算…

阳光电源社招前程无忧智鼎题库及远程包过助攻需要重点考察什么?

阳光电源社招前程无忧智鼎题库及远程包过助攻需要重点考察什么&#xff1f; 结合长期服务大型国有企业校招工作的经验&#xff0c;我们总结出阳光电源社招笔试的典型模式&#xff1a;行政职业能力测试企业应知应会测试心理测评&#xff0c;综合考察候选人的政治素养、文化素养…

VC2022 + protobuf

google这是有私心啊&#xff0c;protobuf从某个版本开始&#xff0c;依赖了一个google自己推出的大型组件集&#xff0c;Abseil&#xff0c;有点类似于Boost了&#xff0c;业内用的人&#xff0c;从个人狭窄的圈子来说&#xff0c;应该是不多的&#xff0c;据说google的众贤用的…

【UnityRPG游戏制作】RPG项目的背包系统商城系统和BOSS大界面

&#x1f468;‍&#x1f4bb;个人主页&#xff1a;元宇宙-秩沅 &#x1f468;‍&#x1f4bb; hallo 欢迎 点赞&#x1f44d; 收藏⭐ 留言&#x1f4dd; 加关注✅! &#x1f468;‍&#x1f4bb; 本文由 秩沅 原创 &#x1f468;‍&#x1f4bb; 收录于专栏&#xff1a;Uni…

【C++】简易二叉搜索树

目录 一、概念&#xff1a; 二、代码实现&#xff1a; 大致结构&#xff1a; 1、遍历&#xff1a; 2、insert 3、find 4、erase 三、总结&#xff1a; 一、概念&#xff1a; 二叉搜索树又称为二叉排序树&#xff0c;是一种具有特殊性质的二叉树&#xff0c;对于每一个节…

springboot+springsecurity+vue前后端分离权限管理系统

有任何问题联系本人QQ: 1205326040 1.介绍 优秀的权限管理系统&#xff0c;核心功能已经实现&#xff0c;采用springbootvue前后端分离开发&#xff0c;springsecurity实现权限控制&#xff0c;实现按钮级的权限管理&#xff0c;非常适合作为基础框架进行项目开发。 2.效果图…

ICP点云配准初探

ICP点云配准初探 1 简介2 常用的点云配准算法3 ICP&#xff08;Iterative Closest Point&#xff0c;最近点迭代法&#xff09;3.1 ICP要解决的问题3.2 ICP的核心思想3.3 算法流程3.4 总结 4 ICP优缺点 1 简介 在逆向工程&#xff0c;计算机视觉&#xff0c;文物数字化等领域中…

香港BTC、ETH现货ETF同时通过,对行业意义几何?

香港比美国更快一步通过以太坊现货 ETF。 2024 年 4 月 15 日&#xff0c;香港嘉实国际资产管理有限公司&#xff08;Harvest Global Investments&#xff09;今天宣布&#xff0c;得到香港证监会的原则上批准&#xff0c;将推出两大数字资产&#xff08;比特币及以太坊&#…

​可视化大屏C位图:园区鸟瞰

将园区鸟瞰图作为可视化大屏设计的焦点图有以下几个好处&#xff1a; 提供全局视图&#xff1a;园区鸟瞰图可以展示整个园区的布局和结构&#xff0c;提供全局视图。这对于大型园区或复杂的场所来说尤为重要&#xff0c;用户可以一目了然地了解整个园区的规模、分布和关联关系…