源码分析CompletableFuture使用默认线程池ForkJoinPool的弊端(cpu利用率低)

4 篇文章 0 订阅
订阅专栏

在这里插入图片描述

先说结论:
假如有20CompletableFuture任务并发执行时,都使用默认线程池ForkJoinPool,但cpu的核心数又小于3,那么就会新建20个线程(不使用默认线程池了),这20个线程相互竞争cpu资源和内存,很多线程都在等待,浪费了大量的性能在线程上下文切换上。

Brian Goetz在《Java并发编程实战》建议
N threads = N CPU * U CPU * (1 + W/C)

  • N CPU 是处理器的核的数目,可以通过 Runtime.getRuntime().availableProce-
    ssors() 得到
  • U CPU 是期望的CPU利用率(该值应该介于0和1之间)
  • W/C是等待时间与计算时间的比率

线程池大小设定:核心数 * 期待利用率 *(1 + 等待时间与计算时间的比率)

  • 如果服务是cpu密集型的,设置为电脑的核数
  • 如果服务是io密集型的,设置为电脑的核数*2

runAsync方法点进去

    CompletableFuture<Void> voidCompletableFuture = CompletableFuture.runAsync(() -> {
         System.out.println(1);
    });

可以看见使用的是asyncPool。点进asyncPool

    public static CompletableFuture<Void> runAsync(Runnable runnable) {
        return asyncRunStage(asyncPool, runnable);
    }

useCommonPool是否为true决定了使用 ForkJoinPool线程池还是新建一个线程池。点进useCommonPool。

    private static final Executor asyncPool = useCommonPool ?
        ForkJoinPool.commonPool() : new ThreadPerTaskExecutor();

这里判定的是ForkJoinPool common线程池中并行度级别是否大于1。点进 getCommonPoolParallelism() 方法

    private static final boolean useCommonPool =
        (ForkJoinPool.getCommonPoolParallelism() > 1);

返回的是commonParallelism这个字段,再往下找。

    public static int getCommonPoolParallelism() {
        return commonParallelism;
    }

发现只有一个地方对这个属性进行赋值,继续。

    static final int commonParallelism;

在这里插入图片描述

发现commonParallelism 由par决定,par来自common.config SMASK做与运算。SMASK定义为0xffff(65535),common.config由 makeCommonPool()得到。点进makeCommonPool()方法

...
static final ForkJoinPool common;
static {
    ...
    common = java.security.AccessController.doPrivileged
      (new java.security.PrivilegedAction<ForkJoinPool>() {
          public ForkJoinPool run() {
            return makeCommonPool(); //common的config由此方法返回
          }});
    int par = common.config & SMASK; // report 1 even if threads disabled
    commonParallelism = par > 0 ? par : 1;
}

在这里插入图片描述

我简化了下面源码,parallelism 初始化为 -1,若jvm启动参数有java.util.concurrent.ForkJoinPool.common那么parallelism将会被启动参数指定。Runtime.getRuntime().availableProcessors() 是获取虚拟机可使用的处理器数量。在jvm未定义参数的前提下,处理器数量若小于等于2,那么并行度parallelism就为1,反之则为 (处理器数量 - 1)。定义了jvm参数,若参数值大于MAX_CAP(32767),则重新赋值。即parallelism 总会大于0, 继续点进ForkJoinPool的构造方法。

    private static ForkJoinPool makeCommonPool() {
        ...
        int parallelism = -1;
        try {  // ignore exceptions in accessing/parsing properties
            String pp = System.getProperty
                ("java.util.concurrent.ForkJoinPool.common.parallelism");
            if (pp != null)
                parallelism = Integer.parseInt(pp);
        } catch (Exception ignore) {
        }
        if (parallelism < 0 && // default 1 less than #cores
            (parallelism = Runtime.getRuntime().availableProcessors() - 1) <= 0)
            parallelism = 1;
        if (parallelism > MAX_CAP)
            parallelism = MAX_CAP;
        return new ForkJoinPool(parallelism, factory, handler, LIFO_QUEUE, // 注意 LIFO_QUEUE等于0 
                                "ForkJoinPool.commonPool-worker-");
    }

发现config也是作位运算,即config也会大于0,我们拿到这个config返回开始 par 赋值那一块。

    private ForkJoinPool(int parallelism,
                         ForkJoinWorkerThreadFactory factory,
                         UncaughtExceptionHandler handler,
                         int mode,
                         String workerNamePrefix) {
        this.workerNamePrefix = workerNamePrefix;
        this.factory = factory;
        this.ueh = handler;
        this.config = (parallelism & SMASK) | mode; // SMASK等于65535,mode等于0
        long np = (long)(-parallelism); // offset ctl counts
        this.ctl = ((np << AC_SHIFT) & AC_MASK) | ((np << TC_SHIFT) & TC_MASK);
    }

总结就是并行度 parallelism(大于0) 与 65535(111111…) 做两次与运算,即本身。继续回到之前

...
static final ForkJoinPool common;
static {
    ...
    common = java.security.AccessController.doPrivileged
      (new java.security.PrivilegedAction<ForkJoinPool>() {
          public ForkJoinPool run() {
            return makeCommonPool(); //common的config由此方法返回
          }});
    int par = common.config & SMASK; // report 1 even if threads disabled
    commonParallelism = par > 0 ? par : 1;
}

这里判断,

  • 无JVM参数前提下:
    • 若服务器的核心数小于等于2,commonParallelism 则为1,即useCommonPool 为false,new 一个线程池。
    • 若服务器的核心数大于2,commonParallelism 则为 核心数 - 1,即useCommonPool 为true,使用ForkJoinPool线程池。
  • 有JVM参数,以设置参数为准。大于1小于等于32767。和上面判断一致。
    private static final Executor asyncPool = useCommonPool ?
        ForkJoinPool.commonPool() : new ThreadPerTaskExecutor();

    private static final boolean useCommonPool =
        (ForkJoinPool.getCommonPoolParallelism() > 1);
        
    public static int getCommonPoolParallelism() {
        return commonParallelism;
    }

在ThreadPerTaskExecutor 中 execute,他会为每个任务新开一个线程,而不是采用ForkJoinPool中的线程。

    static final class ThreadPerTaskExecutor implements Executor {
        public void execute(Runnable r) { new Thread(r).start(); }
    }

结论:jvm启动参数中 java.util.concurrent.ForkJoinPool.common 的值为 1或者服务器核心数小于等于2,都会导致不采用ForkJoinPool 中的线程,而是新起一个线程。

如果服务器只有两核,假如业务需要:现在起了20个completablefuture任务,若使用默认线程池,那么就会创建20个线程,20个线程并发执行在两个cpu上竞争稀缺的处理器和内存资源,浪费大量的时间在上下文切换上。

生产问题之CompletableFuture默认线程池踩坑,请务必自定义线程池
java_2017_csdn的博客
04-24 4050
CompletableFuture是否使用默认线程池的依据,和机器的CPU核心数有关。当CPU核心数-1大于1时,才会使用默认线程池,否则将会为每个CompletableFuture的任务创建一个新线程去执行。
CompletableFutureForkJoinPool
u013078871的博客
04-11 994
package com.lyr.demo.controller; import cn.hutool.core.collection.ListUtil; import cn.hutool.core.thread.ThreadUtil; import cn.hutool.core.util.RandomUtil; import lombok.extern.slf4j.Slf4j; import java.util.ArrayList; import java.util.Collections; import
穿黑风衣的牛奶--生产问题之CompletableFuture默认线程池踩坑,请务必自定义线程池
最新发布
勿忘初心
08-09 1136
使用CompletableFuture一定要自定义线程池CompletableFuture是否使用默认线程池和机器核心数有关,当核心数减1大于1时才会使用默认线程池,否则将为每个任务创建一个新线程去处理即便使用到了默认线程池,池内最大线程数也是核心数减1,对io密集型任务是远远不够的,会令大量任务等待,降吞吐率ForkJoinPool比较适用于CPU密集型的任务,比如说计算。
记一次生产问题--CompletableFuture默认线程池
热门推荐
u011381576的博客
04-20 2万+
在jdk7中,我们使用线程池可能会使用ExecutorService,默认有四种方式 Executors.newSingleeThreadPool() Executors.newFixedThreadPool() Executors.newCacheThreadPool() Executors.newScheduledThreadPool() 在jdk8中,CompletableF...
parallelStream、CompletableFuture 使用默认ForkJoinPool.commonPool()线程池的问题
z69183787的专栏
06-29 5497
parallelStream和CompletableFuture 默认使用的都是ForkJoinPool.commonPool()默认线程池; 基于服务器内核的限制,如果你是八核,每次线程只能起八个,不能自定义线程池; 适用于对list密集计算操作充分利用CPU资源,如果需要调用远端服务不建议使用 参考: https://blog.csdn.net/z69183787/article/details/107026179 https://blog.csdn.net/z69183787/a...
ForkJoinPool
Xiao_xiaoxiao
02-17 437
ForkJoinPool补充为什么不适合执行有block比如有io的任务 ForkJoinPool的优势在于,可以充分利用多cpu,多核cpu的优势,把一个任务拆分成多个“小任务”,把多个“小任务”放到多个处理器核心上并行执行;当多个“小任务”执行完成之后,再将这些执行结果合并起来即可。 ForkJoinPool初略分析 多线程 ForkJoinPool 补充 为什么不适合执行有block比如有i...
JUC - 多线程之ForkJoin;异步调用CompletableFuture(六)
MinggeQingchun的博客
10-22 1277
ForkJoin是在Java7提供的一个用于并行执行任务的框架,ForkJoin 从字面上看Fork是分岔的意思,Join是结合的意思,核心思想就是其实现思想与MapReduce有异曲同工之妙ForkJoin体系中最为关键的就是ForkJoinTask和ForkJoinPoolForkJoin就是利用分治的思想将大的任务按照一定规则Fork拆分成小任务,再通过Join聚合起来ForkJoin最经典的一个应用就是Java8中的,我们知道Stream分为,其中并行流。
ForkJoinPool 使用的错误写法
u011385186的专栏
01-23 4292
compute分出多个task后:以task1,task2为例 错误做法: 1)依次执行task1.fork(),task2.fork() 2)依次执行task1.join(),task2.join() 正确做法: 1)直接调用invokeAll(task1,task2) fork(): 把task置入当前ForkJoinWorkerThread的queue中,等待被消费
async spring 默认线程池_Spring boot注解@Async线程池实例详解
weixin_42313675的博客
12-24 2842
这篇文章主要介绍了Spring boot注解@Async线程池实例详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下从Spring3开始提供了@Async注解,该注解可以被标注在方法上,以便异步地调用该方法。调用者将在调用时立即返回,方法的实际执行将提交给Spring TaskExecutor的任务中,由指定线程池中的线程执行。1. TaskE...
Java多线程并发-线程池-CompletableFuture
Juinjonn的博客
03-03 3038
线程的工作模式是,首先先要创建线程并指定线程需要执行的业务方法,然后再调用线程的 start() 方法,此时线程就从 NEW(新建)状态变成了 RUNNABLE(就绪)状态,此时线程会判断要执行的方法中有没有 synchronized 同步代码块,如果有并且其他线程也在使用此锁,那么线程就会变为 BLOCKED(阻塞等待)状态,当其他线程使用完此锁之后,线程会继续执行剩余的方法。
Spring boot注解@Async线程池实例详解
08-25
* SimpleAsyncTaskExecutor 的默认线程池默认来一个任务创建一个线程,若系统中不断的创建线程,最终会导致系统占用内存过高,引发 OutOfMemoryError 错误。 7. 自定义线程池应用 自定义线程池可以对系统中线程池...
Java 并发编程:Java 线程池的介绍与使用
栗筝i的博客
08-02 3903
内存泄漏的标准定义是当应用程序不再使用对象时发生的情况,但是垃圾回收器无法将其从工作内存中删除,因为它们仍在被引用。因此,应用程序会消耗越来越多的资源,最终导致致命的。垃圾回收操作需要消耗 CPU、线程、时间等资源,所以容易理解的是垃圾回收操作不是实时发生,当内存消耗完或者是达到某一个指标,才能触发垃圾回收操作。在 JVM 堆中,有两种类型的对象——引用和未引用,垃圾回收器可以删除未被引用的对象。即使不再被应用程序使用,也不会收集被引用的对象。
Java 多线程中的任务分解机制-ForkJoinPool,以及CompletableFuture
yelvens的博客
03-25 1503
https://www.cnblogs.com/hongdada/p/8876028.html 1 ForkJoinPool的优势在于,可以充分利用多cpu,多核cpu的优势,把一个任务拆分成多个“小任务”,把多个“小任务”放到多个处理器核心上并行执行;当多个“小任务”执行完成之后,再将这些执行结果合并起来即可。 Java7 提供了ForkJoinPool来支持将一个任务拆分成多个“小任...
JavaJUC指定线程池的情况下,ForkJoinPool不从1开始
m0_46340675的博客
07-10 111
今天遇到一个奇怪的问题,在学习JUCjava.util.concurrent)时,不指定线程池的情况下,线程是会使用JVM默认ForkJoinPool,但是运行的结果,线程编号并不是从1开始的,而是9,比较好奇为什么会出现这种情况。插一嘴,我这里是在Spring框架里测试的,所以怀疑可能是Spring初始化的时候有线程跑在了这个方法之前,但是后来新建了maven项目,发现还是从9号开始的。开始我怀疑是可能存在之前的线程没有清除干净导致的,但执行后可以说明,并不是。遗留一下,以后有问题再再复述。
JUCForkJoinPool类的理解与应用、CompletableFuture类的理解与应用
turbo小世界
06-14 1102
文章目录ForkJoinPool类 JDK 1.7常用方法ForkJoinTask\ 抽象类RecursiveAction 抽象类RecursiveTask\ 抽象类LongStream 接口案例 使用ForkJoin,并行Stream计算大数据的和CompletableFuture\类 异步回调 JDK1.8构造方法常用方法 ForkJoinPool类 JDK 1.7 ForkJoin 并发执行任务,提高效率,在大数据量表现显著。最适合的是计算密集型的任务。 ForkJoin工作原理 是将大量的数据分
CompletableFuture生产中使用必须注意的问题
weixin_42202992的博客
07-29 424
CompletableFuture生产中使用必须注意的问题
【构建并发程序】2-线程池-的注意事项与缺点
大白
09-03 805
线程池-的注意事项与缺点;不写shutdown为什么堵塞:因为是守护线程,main线程没退出,守护线程也不会“退出”, awaitTermination不会退出,所以就在这堵塞了shutdown放在其前面,awaitTermination等待期间,shutdown也在等待任务的结束。2. 因为ForkJoinPool(Executor)对象中的线程是守护线程,因此shutdown要与awaitTermination组合使用,要不然主线程很快结束,线程还没执行呢,也就结束了。
JAVA8中并发类CompletableFuture使用遇到的坑-守护线程
pp_lan的博客
03-24 1万+
1. 前言 最近在看Java8中对并发的支持CompletableFuture类,觉得挺好的,在单一线程执行的时候可以省略很多代码,手动验证后,发现其中还是有一部分坑的,此处记录一下。此处主要描述其创建线程为守护线程的问题,会随着主线程消亡直接消亡,导致任务失败。 2. CompletableFuture优点 简洁,后面以代码为例 3. 案例简述 3.1 CompletableFuture创建一个线程,内部进行轮询读取任务(如:模拟kafka之类的),会发现,执行结束后,整个线程一起关闭了,无法打
CompletableFuture-线程池运行选择
兴趣是最好的老师
09-28 208
调用thenRunAsync执行第二个任务时,则第一个任务使用的是你自定义的线程池,调用thenRun方法执行第二个任务时,则第二个任务和第一个任务时共用同一个线程池。如果没有传入自定义线程池,都用默认线程池ForkJoinPool。传入一个线程池,如果你执行第一个任务时,传入了一个自定义线程池,可能是线程处理太快,系统优化切换原则, 直接使用main线程处理。第二个任务使用的是ForkJoin线程池
详细分析Java使用FFmpeg合并视频和音频文件的优势和弊端
05-21
Java使用FFmpeg合并视频和音频文件的优势和弊端如下: 优势: 1. 跨平台性:Java可以在多种操作系统上运行,而FFmpeg也是跨平台的,因此使用Java和FFmpeg进行视频和音频合并可以在不同的操作系统上使用。 2. 高效性...
写文章

热门文章

  • 尚医通笔记(含bug修改方法) 10293
  • ElasticSearch DSL语句(bool查询、算分控制、地理查询、排序、分页、高亮等) 1957
  • kubernetes(k8s)安装、集群搭建、可视化界面、完全卸载 1653
  • Cause: com.baomidou.mybatisplus.core.exceptions.MybatisPlusException: 非法SQL,SQL未使用到索引, table:user, 1598
  • 源码分析CompletableFuture使用默认线程池ForkJoinPool的弊端(cpu利用率低) 1541

分类专栏

  • JVM 13篇
  • docker 8篇
  • 算法 8篇
  • JUC 4篇
  • ElasticSearch 4篇
  • spring 1篇
  • 工具 1篇
  • kubernetes 3篇
  • 开发笔记 3篇
  • 实战项目 2篇
  • nginx 1篇

最新评论

  • 解决HttpURLConnection GET封装请求体

    行者雪.: 完美解决了问题,很秀

  • 尚医通笔记(含bug修改方法)

    哪里的船迷醉了夕阳: 老哥有没有在远程调用的时候出现feign.FeignException$BadGateway: status 502 reading 这块我检查了单独模块是可以调用的,但是使用feign调用就会报错

  • Spring事务传播机制、实现方式、失效场景及原理

    CSDN-Ada助手: 恭喜你这篇博客进入【CSDN月度精选】榜单,全部的排名请看 https://bbs.csdn.net/topics/617172607。

  • Spring事务传播机制、实现方式、失效场景及原理

    CSDN-Ada助手: 恭喜你这篇博客进入【CSDN每天值得看】榜单,全部的排名请看 https://bbs.csdn.net/topics/616950275。

最新文章

  • 动态规划——背包问题
  • 对象内存布局与对象头
  • kubernetes(namespace、pod、deployment、service、ingress)
2023年31篇
2022年19篇
2021年2篇

目录

目录

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值

深圳坪山网站建设公司网站优化托管方案文库钟楼区网站优化渠道四川网站优化推广服务网站结构的优化主要是什么亦庄企业网站推广优化殷都区网站优化外包博客来达网站优化怎么设置平顶山湖南网站优化推广兰州排名优化网站网站产品优化方案京山网站排名优化网站怎么优化你知道易速达济南网站优化哪家强如何优化推广网站的中山外贸网站优化效果好吗武汉网站优化价格便宜白云公司网站优化推广费用开原seo网站优化效果好不好无为网站优化价格邯郸服务好的婚纱摄影网站优化孟村网站快速优化排名胶南网站推广优化鄂城区网站排名优化报价企业网站优化学习心得体会盖州专业网站优化推广网站的优化都在用易速达芝罘网站优化报价嘉兴网站排行优化网站seo优化关键词怎么选阳阳网站优化关键词排名香港通过《维护国家安全条例》两大学生合买彩票中奖一人不认账让美丽中国“从细节出发”19岁小伙救下5人后溺亡 多方发声卫健委通报少年有偿捐血浆16次猝死汪小菲曝离婚始末何赛飞追着代拍打雅江山火三名扑火人员牺牲系谣言男子被猫抓伤后确诊“猫抓病”周杰伦一审败诉网易中国拥有亿元资产的家庭达13.3万户315晚会后胖东来又人满为患了高校汽车撞人致3死16伤 司机系学生张家界的山上“长”满了韩国人?张立群任西安交通大学校长手机成瘾是影响睡眠质量重要因素网友洛杉矶偶遇贾玲“重生之我在北大当嫡校长”单亲妈妈陷入热恋 14岁儿子报警倪萍分享减重40斤方法杨倩无缘巴黎奥运考生莫言也上北大硕士复试名单了许家印被限制高消费奥巴马现身唐宁街 黑色着装引猜测专访95后高颜值猪保姆男孩8年未见母亲被告知被遗忘七年后宇文玥被薅头发捞上岸郑州一火锅店爆改成麻辣烫店西双版纳热带植物园回应蜉蝣大爆发沉迷短剧的人就像掉进了杀猪盘当地回应沈阳致3死车祸车主疑毒驾开除党籍5年后 原水城县长再被查凯特王妃现身!外出购物视频曝光初中生遭15人围殴自卫刺伤3人判无罪事业单位女子向同事水杯投不明物质男子被流浪猫绊倒 投喂者赔24万外国人感慨凌晨的中国很安全路边卖淀粉肠阿姨主动出示声明书胖东来员工每周单休无小长假王树国卸任西安交大校长 师生送别小米汽车超级工厂正式揭幕黑马情侣提车了妈妈回应孩子在校撞护栏坠楼校方回应护栏损坏小学生课间坠楼房客欠租失踪 房东直发愁专家建议不必谈骨泥色变老人退休金被冒领16年 金额超20万西藏招商引资投资者子女可当地高考特朗普无法缴纳4.54亿美元罚金浙江一高校内汽车冲撞行人 多人受伤

深圳坪山网站建设公司 XML地图 TXT地图 虚拟主机 SEO 网站制作 网站优化