本文将介绍线程池使用不当的五个坑,以及如何避免和解决它们,大纲如下,坑一:线程池中异常消失线程池执行方法时要添加异常处理,这是一个老生常谈的问题,可是直到最近我都有同事还在犯这个错误,所以我还是要讲一下 对于线程池的 submit 方法是无法处理的。坑二:拒绝策略设置错误导致接口超时在 Java 中,线程池拒绝策略可以说一个常见八股文问题。 坑三:重复创建线程池导致内存溢出不知道大家有没有犯过这个问题,不过我确实犯过,归根结底还是写代码前,没有思考好业务逻辑,直接动手,写一步算一步 。所以说写代码的前的一些逻辑梳理、拆分、代码设计很重要。 坑五:使用 ThreadLocal 和线程池的不兼容问题ThreadLocal 是 Java 提供的一个工具类,它可以让每个线程拥有自己的变量副本,从而实现线程间的数据隔离,比如存储一些线程相关的上下文信息 总结本文给大家介绍了线程池使用不当的五个坑,分别是线程池中异常消失、线程池决绝策略设置错误、重复创建线程池导致内存溢出、使用同一个线程池执行不同类型的任务、使用 ThreadLocal 和线程池的不兼容问题
很多人在实际项目中都会有类似这样的问题:我的项目中多个业务需要用到线程池,是为每个线程池都定义一个还是说定义一个公共的线程池呢? 父任务等待子任务执行完成,而子任务等待父任务释放线程池资源,这也就造成了 "死锁" 线程池使用不当导致死锁 解决方法也很简单,就是新增加一个用于执行子任务的线程池专门为其服务。 6 线程池使用的一些小坑 重复创建线程池的坑 线程池是可以复用的,一定不要频繁创建线程池比如一个用户请求到了就单独创建一个线程池。 Spring 内部线程池的坑 使用 Spring 内部线程池时,一定要手动自定义线程池,配置合理的参数,不然会出现生产问题(一个请求创建一个线程)。 ; } } 线程池和 ThreadLocal 共用的坑 线程池和 ThreadLocal共用,可能会导致线程从ThreadLocal获取到的是旧值/脏数据。
前言 线程池是 Java 中处理多线程的强大工具,但它不仅仅是“直接用就完事”的工具。 很多小伙伴在用线程池时,因为配置不当或忽略细节,踩过许多坑。 今天跟大家一起聊聊线程池中容易踩的 10 个坑,以及如何避免这些坑,希望对你会有所帮助。 1. 忽略任务队列的选择 任务队列直接影响线程池的行为。如果选错队列类型,会带来很多隐患。 常见队列的坑 无界队列:任务无限堆积。 有界队列:队列满了会触发拒绝策略。 未监控线程池状态 很多人用线程池后,不监控其状态,导致任务堆积、线程耗尽的问题被忽略。 总结 线程池是强大的工具,但如果我们日常工作中用得不好也非常容易踩坑。 这篇文章通过实际代码示例,我们可以清楚看到线程池的问题所在及改进方法。 希望这些内容能帮你避免踩坑,写出高质量的线程池代码!
前言 大家好,我是捡田螺的小男孩。 日常开发中,为了更好管理线程资源,减少创建线程和销毁线程的资源损耗,我们会使用线程池来执行一些异步任务。但是线程池使用不当,就可能会引发生产事故。 今天田螺哥跟大家聊聊线程池的10个坑。 大家看完肯定会有帮助的~ 线程池默认使用无界队列,任务过多导致OOM 线程创建过多,导致OOM 共享线程池,次要逻辑拖垮主要逻辑 线程池拒绝策略的坑 Spring内部线程池的坑 使用线程池时,没有自定义命名 线程池参数设置不合理 线程池异常处理的坑 使用完线程池忘记关闭 ThreadLocal与线程池搭配,线程复用,导致信息错乱。 线程池拒绝策略的坑,使用不当导致阻塞 我们知道线程池主要有四种拒绝策略,如下: AbortPolicy: 丢弃任务并抛出RejectedExecutionException异常。
引言在Java多线程编程中,线程池是提高性能和资源利用率的常用工具。然而,当父子任务使用同一线程池时,可能导致潜在的死锁问题。 2的线程池, 然后向线程池提交任务, 这个任务直接调用outerTask, 这个outerTask不做任何事情, 只通过线程池异步调用innerTask, 但是注意这里使用了同一个线程池提交innerTask outerTask提交任务的地方, 同时通过查看线程池的workQueue对象可以看到有很多任务堆积:原因分析子任务需要等待父任务完成,而父任务内部的子任务通过同一个线程池提交,又需要等待线程池有空闲线程才能得到执行 (在我的公司实际发生过这种故障,开发不停重启和扩容但过一段时间仍然会发生这个问题,排查了很长时间才发现问题原因)解决方案为避免父子任务使用同一线程池造成死锁,可以考虑使用独立线程池:将父任务和子任务分别提交到不同的线程池 ,避免共享线程池资源,减少死锁的可能性。
文章目录 概述 问题复现 源码分析 解决办法 小结 概述 先说结论 线程池使用FutureTask时如果把拒绝策略设置为 DiscardPolicy和 DiscardOldestPolicy,并且在被拒绝的任务的 代码(1)创建了一个单线程和一个队列元素个数为1的线程池,并且把拒绝策略设置为 DiscardPolicy。 代码(2)向线程池提交了一个异步任务one,并且这个任务会由唯一的线程来执行,任务在打印【开始处理业务1】 后会阻塞该线程2s。 代码(3)向线程池提交了一个异步任务two,这时候会把任务two放入阻塞队列。 代码(4)向线程池提交任务three,由于队列已满所以触发拒绝策略丢弃任务three。 任务one执行完成后线程池的唯一线程会去队列里面取出任务two并执行,所以输出【开始处理业务2】,然后代码(6)返回,这时候主线程输出【任务2 null】。
1.线程池的好处。 线程使应用能够更加充分合理的协调利用cpu 、内存、网络、i/o等系统资源。 线程的创建需要开辟虚拟机栈,本地方法栈、程序计数器等线程私有的内存空间。 所以需要通过线程池协调多个线程,并实现类似主次线程隔离、定时执行、周期执行等任务。线程池的作用包括: 利用线程池管理并复用线程、控制最大并发数等。 实现任务线程队列缓存策略和拒绝机制。 比如,交易服务和搜索服务在同一台服务器上,分别开启两个线程池,交易线程的资源消耗明显要大;因此,通过配置独立的线程池,将较慢的交易服务与搜索服务隔开,避免个服务线程互相影响。 在了解线程池的基本作用后,我们学习一下线程池是如何创建线程的。 线程工厂需要做创建前的准备工作,对线程池创建的线程必须明确标识,就像药品的生产批号一样,为线程本身指定有意思的名称和相应的序列号。
像这种,提前创建好线程,需要的时候直接使用,我们称之为线程池。这种本质上就是一个生产消费模型。 线程池实现 //ThreadPool.hpp #pragma once #include<iostream> #include<unistd.h> #include<string> #include< //计数器:休眠的线程个数 pthread_mutex_t _mutex; pthread_cond_t _cond; }; 日志 日志是软件运行的记录信息,可以向显示器打印,也可以向文件中打印 lg.Enable(SCREEN_TYPE);}while(0) #define EnableFile() do{lg.Enable(FILE_TYPE);}while(0) }; 携带日志的线程池设计 Task>(); tp->Init(); tp->Start(); int cnt=10; while (cnt) { // 不断地向线程池推送任务
int corePoolSize = 2; /* 核心线程池的最大线程数 */ int maxPoolSize = 4; /* 线程最大空闲时间 */ 不推荐使用Executors的静态方法创建线程池 !!! 第2节 ForkJoinPool ---- ForkJoin线程池处理无返回值任务。 ForkJoinPool-1-worker-7的i值 2995 ······ RESULT_SET的大小=3000 ForkJoin线程池处理有返回值任务。 初始化数组用时:1847192纳秒, 初始化数组总和:493016 线程池计算用时:4220889纳秒, 线程池执行结果:493016 ?
然后给我指出了一个问题,我仔细思考了一下,好像确实是留了一个坑。 为了更好的描述这个坑,我先给大家回顾一下线程池动态调整的几个关键点。 首先,为什么需要对线程池的参数进行动态调整呢? 那么线程池可以修改的参数有哪些呢? 正常来说是可以调整核心线程数和最大线程数的。 线程池也直接提供了其对应的 set 方法: 但是其实还有一个关键参数也是需要调整的,那就是队列的长度。 第二种方式,大多适用于那种“你也不知道为什么,反正这样写程序就是正常了”的情况。 现在我们知道在线程池里面动态调整队列长度的坑是什么了。 然后 test 方法里面是每次都搞一个新的线程池,接着往线程池里面提交队列长度加最大线程数个任务,最后关闭这个线程池。 同时还有另外一个线程把线程池的核心线程数从 1 修改为 5。 调试多线程的时候,最好是不要使用 System.out.println,有坑! 场景 我们再回头看看老爷子给出的方案: 其实它给了两个。
大家好,又见面了,我是你们的朋友全栈君。 使用 ThreadPoolExecutor 创建线程池 源码分析 ,ThreadPoolExecutor 的构造函数 public ThreadPoolExecutor(int corePoolSize 如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
1.线程池的作用 【线程池】就是用来存放【线程】的对象池。 在程序的世界里,如果创建某种对象所需要的代价太高,同时这个对象又可以反复使用,那么我们往往就会准备一个容器,用来保存一批这样的对象。 相信上面这段文字也已经讲清了“线程池”的作用:因为创建一个线程的代价较高,因此我们使用线程池设法复用线程。就是这么简单。 而CLR线程池便是存放这些CLR线程的对象池。ASP.NET在得到一个请求后,也会将这个请求处理的任务交由CLR线程池去执行——请注意,它们最多只是添加任务而已,并不表示任务会立即执行。 简单的概括说来,便是线程池内有空闲的线程,或线程池所管理的线程数量还没有达到上限的时候。如果有空闲的线程,线程池就会立即让它领取一个任务执行。如果是第二种情况,线程池便会创建新的Thread对象。 因此,CLR线程池在使用大量线程处理完大量任务之后,也会逐步地释放线程,直至到达最小值。CLR线程池的最小线程数量确保了在任务数量较少的情况下,新来的任务可以立即执行,从而省去了创建新线程的时间。
线程池 线程池和数据库的连接池是同样意思,把多个线程放在一个集合里,有任务时从集合里分配线程,当该线程完成任务后不是销毁,放入线程池等待下次任务,减少了创建和销毁线程的次数,提高系统效率,因为创建和销毁属于重操作 ThreadPoolExecutor推荐使用的线程池类 后面发现有个ForkJoinPool 线程池类,从1.7开始有的,不做讨论了 ? ThreadPoolExecutor 这个常用的类提供了创建线程池的方法,根据传入的参数不同,创建不同的线程池,先来看看构造方法 public ThreadPoolExecutor( 线程池的状态 RUNNING:线程池能接受新任务,以及对新添加的任务进行处理 SHUTDOWN:线程池不接受新任务,但会对已添加的任务进行处理 STOP:线程池不接收新任务,不处理已添加的任务,并且会中断正在处理的任务 类中是空的,若用户想在线程池变为TIDYING时,进行相应的处理,可以通过重载terminated()函数来实现 TERMINATED:线程池真正的终止 5.
建议不同类别的业务用不同的线程池 很多人在实际项目中都会有类似这样的问题:我的项目中多个业务需要用到线程池,是为每个线程池都定义一个还是说定义一个公共的线程池呢? 最终实现的可动态修改线程池参数效果如下 动态配置线程池参数最终效果 如果我们的项目也想要实现这种效果的话,可以借助现成的开源项目: Hippo-4 :一款强大的动态线程池框架,解决了传统线程池使用存在的一些痛点比如线程池参数没办法动态修改 线程池使用的一些小坑 重复创建线程池的坑 线程池是可以复用的,一定不要频繁创建线程池比如一个用户请求到了就单独创建一个线程池。 Spring 内部线程池的坑 使用 Spring 内部线程池时,一定要手动自定义线程池,配置合理的参数,不然会出现生产问题(一个请求创建一个线程)。 ; } } 线程池和 ThreadLocal 共用的坑 线程池和 ThreadLocal共用,可能会导致线程从ThreadLocal获取到的是旧值/脏数据。
还拿上边的例子说,如果我们使用线程池的方式的话,可以实现最多创建爱你线程的数量,这样的话就算再多的数据需要入库,只需要排队等待线程池的线程即可,就不会出现线程池过多而消耗系统资源的情况,当然这只是意见简单的场景 那么什么是线程池? 为了避免系统频繁的创建和销毁线程,我们可以将创建的线程进行复用。数据库中的数据库连接池也是此意。 ? 简而言之:创建线程变成了从线程池获取空闲的线程,关闭线程变成了向池子中归还线程。 再多的概念,不过多解释,因为很基础,也不是本文的重点。 JDK对线程池的支持 JDK提供的Eexecutor框架 JDK提供了Executor框架,可以让我们有效的管理和控制我们的线程,其实质也就是一个线程池。 更多实例代码,可参考: https://gitee.com/xuliugen/codes/ta5dbsge0kvhy62qu8li157 使用submit的坑 首先看一下实例: ? 运行结果: ?
Java线程池的大小与线程池死锁 优化线程池大小 线程池大小对系统性能是有一定影响的,过大或者过小都会无法发挥最优的系统性能, 线程池大小不需要非常精确,只要避免极大或者极小的情况即可, 一般来说,线程池大小需要考虑 在书中给出一个估算线程池大小的公式: 线程池大小 = CPU的数量 * 目标CPU的使用率*( 1 + 等待时间与计算时间的比) 线程池死锁 如果在线程池中执行的任务A在执行过程中又向线程池提交了任务B , 任务B添加到了线程池的等待队列中, 如果任务A的结束需要等待任务B的执行结果. 适合给线程池提交相互独立的任务,而不是彼此依赖的任务. 对于彼此依赖的任务,可以考虑分别提交给不同的线程池来执行。 Java线程池异常处理 在使用ThreadPoolExecutor进行submit提交任务时,有的任务抛出了异常,但是线程池并没有进行提示,即线程池把任务中的异常给吃掉了,可以把submit提交改为execute
但是在高并发情况下会频繁的创建和销毁线程,这样就变相的阻碍了程序的执行速度,所以为了管理线程资源和减少线程创建以及销毁的性能消耗就引入了线程池。 2.什么场景下适合使用线程池 当服务器接收到大量任务时,如果使用线程池可以大量减少线程的创建与销毁次数,从而提升程序执行效率 在实际开发中,如果需要创建5个以上的线程,那么就可以使用线程池来管理 3.线程池参数介绍以及特点 3.3 增减线程的特点 将corePoolSize和maxPoolSize设置为相同的值,那么就会创建固定大小的线程池。 线程池希望保持更少的线程数,并且只有在负载变得很大时才会增加它。 如果将线程池的maxPoolSize参数设置为很大的值,例如Integer.MAX_VALUE,可以允许线程池容纳任意数量的并发任务。 4.线程池应该手动创建还是自动创建 手动创建更好,因为这样可以让我们更加了解线程池的运行规则,避免资源耗尽的风险。
(); // 创建固定长度的线程池,比如4个 var expool = Executors.newFixedThreadPool(4); // 创建弹性可伸缩的线程池 Executors.newCachedThreadPool ==如果任务满载最大线程池且设置的队列中也满了,则执行这个拒绝策略处理超期的任务== 当线程空闲超过keepAliveTime,非核心线程会被回收,若allowCoreThreadTimeOut为true (jdk默认策略) CallerRunsPolicy -- 当任务添加到线程池中被拒绝时,会在线程池当前正在运行的Thread线程池中处理被拒绝的任务。 DiscardOldestPolicy -- 当任务添加到线程池中被拒绝时,线程池会放弃等待队列中最旧的未处理任务,然后将被拒绝的任务添加到等待队列中。 DiscardPolicy -- 当任务添加到线程池中被拒绝时,线程池将丢弃被拒绝的任务。
大家好,又见面了,我是你们的朋友全栈君。 Executors如何创建线程池? Executors 类是从 JDK 1.5 开始就新增的线程池创建的静态工厂类,它就是创建线程池的,但是很多的大厂已经不建议使用该类去创建线程池。 1. newFixedThreadPool,创建定长线程池,每当提交一个任务就创建一个线程,直到达到线程池的最大数量,这时线程数量不再变化,当线程发生错误结束时,线程池会补充一个新的线程。 3 的线程池,每当提交一个任务就创建一个线程,直到达到线程池的最大数量,这时线程数量不再变化,当线程发生错误结束时,线程池会补充一个新的线程 ExecutorService fixedThreadPool ,如果线程池的容量超过了任务数,自动回收空闲线程,任务增加时可以自动添加新线程,线程池的容量不限制。
线程池合适的线程数量 密集型任务 第一种是 CPU 密集型任务,比如加密、解密、压缩、计算等一系列需要大量耗费 CPU 资源的任务。 最佳线程数 = CPU 核心数的 1~2 倍 如果设置过多的线程,实际上并不会起到很好的效果。 此时假设我们设置的线程数是 CPU 核心数的 2 倍以上,因为计算机的任务很重,会占用大量的 CPU 资源,所以这是 CPU 每个核心都是满负荷工作,而设置过多的线程数,每个线程都去抢占 CPU 资源, 而如果我们设置更多的线程数,那么当一部分线程正在等待 IO 的时候,它们此时并不需要 CPU 来计算,那么另外的线程便可以利用 CPU 去执行其他的任务,互不影响,这样的话在任务队列中等待的任务就会减少 太少的线程数会使得程序整体性能降低,而过多的线程也会消耗内存等其他资源,所以如果想要更准确的话,可以进行压测,监控 JVM 的线程情况以及 CPU 的负载情况,根据实际情况衡量应该创建的线程数,合理并充分利用资源