Java JUC并发编程调优

news2024/11/17 3:33:35

前言

1、JUC是指有关 java.util.concurrent包以及其子包,这些包都是有关线程操作的包
2、HTTPS服务请求中,WEB服务只负责创建主线程来接收外部的HTTPS请求,如果不做任何处理,默认业务逻辑是通过主线程来做的,如果业务执行时间较长且用户访问量较大的情况,WEB服务在单位时间内所能处理的用户请求就会有限,JUC并发编程的核心就是如何来释放主线成以及通过子线程来批量执行任务
3、JUC并发编程并不能提高执行任务所耗费的时间,但是可以极大的提高WEB容器的吞吐量,能在单位时间内接收更多的用户请求

案例(模拟下单)

在这里插入图片描述

  • 公共代码(创建订单)
@Service
public class OrderTaskService {
    public JSONObject create() {
        JSONObject jsonObject = new JSONObject();
        jsonObject.put("orderId", "order-1101");
        System.out.println("OrderTaskService子线程:" + Thread.currentThread().getName());
        return jsonObject;
    }
}
  • 公共代码(操作积分)
@Service
public class ItegralFutureTaskService {
    public String operate() {
        try {
            Thread.sleep(500);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        System.out.println("ItegralFutureTaskService子线程:" + Thread.currentThread().getName());
        return "休眠500毫秒后返回";
    }
}
  • 公共代码(操作库存)
@Service
public class StockFutureTaskService {
    public String operate() {
        try {
            Thread.sleep(500);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        System.out.println("StockFutureTaskService子线程:" + Thread.currentThread().getName());
        return "休眠500毫秒后返回";
    }
}

同步处理

@GetMapping("createOrderCommon")
public JSONObject createOrder() {
    long start = System.currentTimeMillis();
    // 本地服务 - 创建订单
    String order = orderTaskService.create();
    // 三方服务 - 处理库存
    String stock = stockFutureTaskService.operate();
    // 三方服务 - 处理积分
    String itegral = itegralFutureTaskService.operate();
    long end = System.currentTimeMillis();

    JSONObject orderInfo = new JSONObject();
    orderInfo.put("order", order);
    orderInfo.put("stock", stock);
    orderInfo.put("itegral", itegral);
    orderInfo.put("耗时", (end - start));
    return orderInfo;
}
{
	"itegral": "积分处理:http-nio-0.0.0.0-8801-exec-2 休眠500毫秒后返回",
	"stock": "库存处理:http-nio-0.0.0.0-8801-exec-2 休眠500毫秒后返回",
	"order": "订单处理:http-nio-0.0.0.0-8801-exec-2"
	"耗时": 1014,
}

分析:模拟环境下,积分处理花费500毫秒,库存处理花费500毫米,再加上订单处理耗时应该在:1000+毫秒,最终WEB容器主线程被占用的时间为1014毫秒

异步处理(FutureTask)

@GetMapping("createOrderFutureTask")
public JSONObject createOrderFutureTask() throws ExecutionException, InterruptedException {
    long start = System.currentTimeMillis();
    JSONObject orderInfo = new JSONObject();

    // 本地服务 - 创建订单
    String order = orderTaskService.create();
    orderInfo.put("order", order);

    // 三方服务 - 处理库存 TODO【异步】
    Callable<String> callableForStock = new Callable<String>() {
        @Override
        public String call() throws Exception {
            return stockFutureTaskService.operate();
        }
    };

    // 三方服务 - 处理积分 TODO【异步】
    Callable<String> callableForItegral = new Callable<String>() {
        @Override
        public String call() throws Exception {
            return itegralFutureTaskService.operate();
        }
    };

    FutureTask<String> futureForStock = new FutureTask<>(callableForStock);
    FutureTask<String> futureForItegral = new FutureTask<>(callableForItegral);
    
    new Thread(futureForStock).start();
    new Thread(futureForItegral).start();

    orderInfo.put("stock", futureForStock.get()); // TODO 阻塞式获取执行结果,所以会占用web容器的主线程
    orderInfo.put("itegral", futureForItegral.get()); // TODO 阻塞式获取执行结果,所以会占用web容器的主线程

    long end = System.currentTimeMillis();
    orderInfo.put("耗时", (end - start));
    System.out.println("任务耗时:" + orderInfo.get("耗时"));
    return orderInfo;
}
{
	"itegral": "积分处理:Thread-23 休眠500毫秒后返回",
	"stock": "库存处理:Thread-22 休眠500毫秒后返回",
	"order": "订单处理:http-nio-0.0.0.0-8801-exec-9"
	"耗时": 508,
}

分析1:模拟环境下,积分处理花费500毫秒,库存处理花费500毫米,但积分处理和库存处理是并发执行的,再加上订单处理耗时应该在:500+毫秒,最终WEB容器主线程被占用的时间为508毫秒
分析2:通过FutureTask实现多线程并发处理时,如果WEB容器主线程fork出来的子线程没有返回执行结果,那么主线程是会一直阻塞,直到子线程返回结果

优化FutureTask

Asynchronous Requests

DeferredResult和Callable都是为了异步生成返回值提供基本的支持。一个请求进来,在没有得到返回数据之前,DispatcherServlet和所有Filter就会退出Servlet容器线程,但响应保持打开状态,一旦返回数据有了,DispatcherServlet就会被再次调用并且处理,以异步产生的方式,向请求端返回值。这么做的好处就是请求不会长时间占用服务连接池,提高服务器的吞吐量

使用Callable对FutureTask优化

@GetMapping("createOrderCallable")
    public Callable<JSONObject> createOrderCallable() throws ExecutionException, InterruptedException {
        long start = System.currentTimeMillis();

        Callable<JSONObject> callable = new Callable<JSONObject>() {
            @Override
            public JSONObject call() throws Exception {
                return createOrderFutureTask();
            }
        };

        long end = System.currentTimeMillis();
        System.out.println("主线程处理耗时:" + Thread.currentThread().getName() + " " +(end - start) + "毫秒");

        return callable;
    }
主线程处理耗时:http-nio-0.0.0.0-8801-exec-1 0毫秒
OrderTaskService子线程:task-1
ItegralFutureTaskService子线程:Thread-15
StockFutureTaskService子线程:Thread-14
任务耗时:540

分析1:从运行日志上可以看出来,任务执行花费了540毫秒,WEB容器的主线称只被占用了0毫秒

异步处理(CompletableFuture)

  • 自定义线程池对象并纳入容器管理
/**
 * @描述: TODO 自定义线程池来处理异步任务。在方法上添加:@Async("api-asyn-taskExecutor")
 * @作者: lixing lixing_java@163.com
 * @日期 2020/2/19 21:18
 */
@Component
@Configuration
public class ThreadPoolTaskExecutorConfig {
    @Bean("api-asyn-taskExecutor")
    public Executor taskExecutorPool() {
        ThreadPoolTaskExecutor threadPoolTaskExecutor = new ThreadPoolTaskExecutor();
        threadPoolTaskExecutor.setThreadNamePrefix("自定义异步线程池"); // 线程名字前缀
        threadPoolTaskExecutor.setCorePoolSize(5); // 核心线程数
        threadPoolTaskExecutor.setQueueCapacity(10); // 任务队列容量(缓存队列)
        threadPoolTaskExecutor.setMaxPoolSize(10); //线程池维护线程的最大数量,只有在缓冲队列满了之后才会申请超过核心线程数的线程
        threadPoolTaskExecutor.setKeepAliveSeconds(120); // 允许空闲时间,当超过了核心线程数之外的线程在空闲时间到达之后会被销毁
        threadPoolTaskExecutor.setRejectedExecutionHandler( new ThreadPoolExecutor.CallerRunsPolicy() ); // 任务拒绝策略
        // 关闭线程池时是否等待当前调度任务完成后才开始关闭
        threadPoolTaskExecutor.setWaitForTasksToCompleteOnShutdown( true ); // 等待
        threadPoolTaskExecutor.setAwaitTerminationSeconds( 60 ); // 等待时长
        threadPoolTaskExecutor.setRejectedExecutionHandler(new RejectedExecutionHandler() {
            @Override
            public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
                // 异常统一处理......发送邮件、发送短信
            }
        });
        threadPoolTaskExecutor.initialize();
        return threadPoolTaskExecutor;
    }

}
  • 实现代码
/**
    * 创建订单:JAVA8新特性
    */
   @Resource
   private ThreadPoolTaskExecutorConfig threadPoolTaskExecutorConfig;
   @GetMapping("createOrderCompletableFuture")
   public JSONObject createOrderCompletableFuture() throws ExecutionException, InterruptedException {
       long start = System.currentTimeMillis();

       CompletableFuture<JSONObject> supplyAsync = CompletableFuture.supplyAsync(() -> {
           return createOrder();
       }, threadPoolTaskExecutorConfig.taskExecutorPool());

       long end = System.currentTimeMillis();
       System.out.println("主线程处理耗时:" + Thread.currentThread().getName() + " " +(end - start) + "毫秒");

       return supplyAsync.get();
   }

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

预训练大语言模型的三种微调技术总结:fine-tuning、parameter-efficient fine-tuning和prompt-tuning

预训练大模型&#xff0c;尤其是大语言模型已经是当前最火热的AI技术。2018年Google发布BERT模型&#xff08;BERT在DataLearner官方模型卡信息&#xff1a;https://www.datalearner.com/ai-models/pretrained-models/BERT &#xff09;的时候&#xff0c;大家还没有意识到本轮…

国际植物命名数据库(International Plant Names Index)

功能介绍 https://www.ipni.org/ 是国际植物命名数据库&#xff08;International Plant Names Index&#xff09;的官方网站。国际植物命名数据库是一个全球性的植物命名和分类资源&#xff0c;旨在提供植物命名信息的权威来源。以下是该网站的一些特点和功能&#xff1a; 植…

wmvcore.dll丢失怎么弄?解决wmvcore.dll丢失问题

wmvcore.dll是Windows Media Player中的一个重要组件&#xff0c;它负责处理视频和音频文件的编码和解码。如果您在使用Windows Media Player或其他媒体播放器时遇到了wmvcore.dll丢失的问题&#xff0c;那么您可能无法播放视频或音频文件。在这种情况下&#xff0c;您可以使用…

干货|接口测试必备技能-常见接口协议解析

【摘要】 服务与服务之间传递数据包&#xff0c;往往会因为不同的应用场景&#xff0c;使用不同的通讯协议进行传递。比如网站的访问&#xff0c;常常会使用 HTTP 协议进行传递&#xff0c;文件传输使用 FTP&#xff0c;邮件传递使用 SMTP。上述的三种类型的协议都处于网络模型…

电流环参数自整定及其原理

前言 电流环参数自整定是通过程序计算电流环PI调节器增益以实现环路响应仅受用户设定PI调节器带宽影响&#xff0c;而和电机本身参数无关的目的。 本文分析电流环参数自整定背后的原理&#xff0c;并通过仿真进行验证。 1、永磁同步电机dq轴数学模型 本文提到的电流环参数自…

【论文总结】Creating a Secure Underlay for the Internet

为互联网创建一个安全的底层 摘要&#xff1a; 对手可以利用跨域路由漏洞拦截通信并破坏关键互联网应用的安全性。与此同时&#xff0c;部署诸如边界网关安全协议&#xff08;BGPsec&#xff09;和下一代网络上的可扩展性、控制和隔离&#xff08;SCION&#xff09;等安全路由…

38 KVM管理设备-管理虚拟机USB

文章目录 38 KVM管理设备-管理虚拟机USB38.1 配置USB控制器38.1.1 概述38.1.2 注意事项38.1.3 配置方法 38.2 配置USB直通设备38.2.1 概述38.2.2 注意事项38.2.3 配置说明38.2.4 配置方法 38 KVM管理设备-管理虚拟机USB 为了方便在虚拟机内部使用USBkey设备、USB海量存储设备等…

蓝桥杯单片机基础模板一:动态数码管显示

蓝桥杯单片机有一些基础模块的写法&#xff0c;他是没有一定的公式模板的。 好比动态数码管扫描显示&#xff0c;原理就是快速地 传输段选和位选。 但因为电路上添加了74HC573芯片&#xff0c;导致了我们移植别处 51单片机的动态扫描代码 会出现诸多问题&#xff0c;比如闪烁…

期末学习总结模板

期末学习总结精选篇1 转眼间研究生的生活已经有一学期了&#xff0c;感觉才参加复试没多久&#xff0c;回头想想&#xff0c;这一年过的真快&#xff0c;因为生病&#xff0c;耽误了很多时间&#xff0c;收获太少&#xff0c;遗憾太多。 本学期的收获&#xff1a; 1、暑假里看了…

Elasticsearch文件存储

分析Elasticsearch Index文件是如何存储的&#xff1f; 主要是想看一下FST文件是以什么粒度创建的&#xff1f; 首先通过kibana找一个索引的shard&#xff0c;此处咱们就以logstash-2023.05.30索引为例 查看下shard分布情况 GET /_cat/shards/logstash-2023.05.30?vindex …

AtCoder Beginner Contest 303——A-E题讲解

蒟蒻来讲题&#xff0c;还望大家喜。若哪有问题&#xff0c;大家尽可提&#xff01; Hello, 大家好哇&#xff01;本初中生蒟蒻讲解一下AtCoder Beginner Contest 303这场比赛的A-E题&#xff01; A - Similar String 原题 Problem Statement Two characters x x x and y…

Python IDLE介绍

目录 IDE&#xff08;集成开发环境&#xff09;是什么 Python IDLE使用方法详解 Python IDLE常用快捷键 IDE&#xff08;集成开发环境&#xff09;是什么 IDE 是 Integrated Development Environment 的缩写&#xff0c;中文称为集成开发环境&#xff0c;用来表示辅助程序员…

一、Fuzzing速成之AFL实战

文章目录 一、Fuzzing基本知识1、什么是Fuzzing?2、Fuzzing运行流程 二、Fuzzing实战1、下载AFL压缩包2、安装AFL3、Fuzzing 一、Fuzzing基本知识 1、什么是Fuzzing? 维基百科将模糊测试定义为&#xff1a;     模糊测试 &#xff08;fuzz testing, fuzzing&#xff09;…

【Python 文本挖掘】零基础也能轻松掌握的学习路线与参考资料

Python文本挖掘是利用Python语言和相关文本挖掘工具对大量文本数据进行分析和挖掘的过程。Python在文本挖掘方面广泛应用于自然语言处理、情感分析、主题建模、关键词提取等领域。 学习Python文本挖掘需要掌握Python编程基础、数据分析和可视化、自然语言处理、机器学习等知识…

Flutter:功能型组件(3)- 拖拽组件、缩放平移组件

拖拽组件 拖拽组件包含 Draggable、LongPressDraggable 和 DragTarget。 Draggable、LongPressDraggable 为可拖拽的组件&#xff0c;LongPressDraggable 继承自Draggable&#xff0c;因此用法和 Draggable 完全一样&#xff0c;唯一的区别就是 LongPressDraggable 触发拖动的…

nginx添加nginx-sticky-module模块步骤

nginx-sticky-module模块是nginx实现负载均衡的一种方案,和ip_hash负载均衡算法会有区别的 ip_hash 根据客户端ip将请求分配到不同的服务器上.sticky 根据服务器个客户端的cookie,客户端再次请求是会带上此cookie,nginx会把有次cookie的请求转发到颁发cookie的服务器上. 安装…

AI工具合集!一共600+覆盖全行业,除了ChatGPT,那你也会喜欢这些其他的AI工具

如果你喜欢ChatGPT&#xff0c;那你也会喜欢这些其他的AI工具。 AI正在改变我们的工作方式&#xff0c;我不想错过充分利用它的机会&#xff0c;所以我尝试了一系列AI工具来节省时间&#xff0c;提高我的工作效率。 这里有个集合了600ai工具的合集包。 序号AI工具名称AI分类A…

I.MX RT1170加密启动详解(4):OTFAD XIP加密运行代码

本节将介绍基于AES加密的OTFAD引擎&#xff0c;它可以在不影响AES-128-CTR性能的情况下实时解密数据。OTFAD包括对AES密钥展开机制的完整硬件支持&#xff0c;它可以解密最多4个唯一的AES上下文。每个上下文都有一个用户定义的128位的Image Encryption Key(IEK)、一个64位的计数…

uniapp的movable-view、movable-area

uniapp的movable-view、movable-area movable-view&#xff1a; 可以在页面中拖拽滑动必须在movable-area组件中&#xff0c;并且必须是直接子节点必须设置width和height属性&#xff0c;不设置默认为10px提供特殊事件&#xff1a;htouchmove和vtouchmove movable-area&#xf…

用于ECharts的全国省市区县乡镇街道级的行政区划边界数据(GeoJSON格式)

https://map.vanbyte.com 提供了免费的省市县3级行政边界数据(GeoJSON格式)、省市县乡4级联动数据。 至于行政区划边界数据的来源&#xff0c;网络上有各种教程。授人以鱼不如授人以渔&#xff0c;下面记录一下各类方法的具体步骤。 来源1&#xff1a;阿里云的数据可视化平台…