多线性开发实例分享

在这里插入图片描述

一. 概述

首先,在这里有必要和大家复现一下我使用该技术的背景:

在使用若依框架的时候,由于实际开发的需要,我需要配置四个数据源,并且通过mapper轮流去查每个库的指定用户数据,从而去判断改库是否存在目标数据,这样,就会产生一个问题,我需要查找四个库,由于网络限制等各方面因素,导致速度特别慢,这个时候我就在想,能不能使用线程并发,在请求一个库的同时,其余单个库的请求也同步进行,这样就会大大节约请求时间,提高效率,不会导致网络请求超时,所以就会出现一个问题,如何去使用多线程技术?

二. 混淆解析

多线程、多进程、异步、并发是计算机中常见的概念,它们都与程序的执行方式和效率相关。

  1. 多线程

多线程是指一个进程中同时运行多个线程,每个线程相对独立地执行不同的任务,共享进程的资源。Java语言提供了Thread类和Runnable接口以实现多线程编程。通过多线程可以提高CPU利用率,加快程序执行速度,但也会带来线程同步等问题。

举例:在Java中,创建新的线程通常使用Thread类或者Runnable接口,如下所示:

public class MyThread extends Thread{
  public void run(){
    // 执行某些操作
  }
}

public class MyRunnable implements Runnable{
  public void run(){
    // 执行某些操作
  }
}

// 创建并启动线程
MyThread thread = new MyThread();
thread.start();

MyRunnable runnable = new MyRunnable();
Thread thread2 = new Thread(runnable);
thread2.start();
  1. 多进程

多进程是指一个程序同时运行多个进程,每个进程有自己的独立地址空间,并与其他进程互相独立。多进程在保证数据隔离的同时,能够充分利用系统的资源,但是进程之间的通信也需要一定的开销和时间代价。

举例:在Java中,可以使用Runtime类和ProcessBuilder类创建新的进程。如下所示:

Runtime.getRuntime().exec("myCommand");

ProcessBuilder pb = new ProcessBuilder("myCommand");
Process process = pb.start();
  1. 异步

异步是指程序的执行方式,即某个操作不会阻塞代码的进程或线程,而是通过回调函数等方式在后台执行。异步通常使用事件驱动或者回调函数来实现。

举例:在Java中,可以使用Future和CompletableFuture类实现异步调用。如下所示:

Future<Integer> future = new CompletableFuture<>();
new Thread(() -> {
    // 执行一些耗时的操作
    int result = 123;
    // 将结果设置到future中
    future.complete(result);
}).start();

// 阻塞并等待结果
int result = future.get();
  1. 并发

并发是指多个任务同时执行,每个任务独立运行,并与其他任务相互交错执行。并发能够提高程序整体执行效率,但也会带来资源占用、线程同步等问题。

举例:在Java中,可以使用synchronized关键字和ReentrantLock类等机制来控制并发访问共享资源。如下所示:

public class Counter {
    private int count;

    public synchronized void increment() {
        count++; // 线程安全的自增操作
    }

    public int getCount() {
        return count; //非线程安全的获取操作
    }
}

总之,多线程、多进程、异步、并发等概念都与程序执行方式和效率相关,并且都需要仔细考虑各种问题,如线程安全、锁竞争等等。开发者需要根据具体业务需求选择合适的技术手段来实现程序。

三. 案例重现

        //辅助对象
        SysUser user = null;
        //培训信息用户
        RancoUser rancoUser = userService.selectUserByUserNameRanco(username);
        //服务信息用户
        SysUser server = userService.selectUserByUserNameServer(username);
        //客户信息用户
        SysUser customer = userService.selectUserByUserNameCustomer(username);
        //知识库用户
        SysUser knowledage = userService.selectUserByUserNameKnowledage(username);
        //判断那个不为空辅助对象为那个
        user = StringUtils.isNotNull(server) ? server : StringUtils.isNotNull(customer) ? customer : StringUtils.isNotNull(knowledage) ? knowledage : null;
        //服务,客户,知识三大模块的用户鉴权
        if (StringUtils.isNotNull(user)) {
            if (UserStatus.DELETED.getCode().equals(user.getDelFlag())) {
                log.info("登录用户:{} 已被删除.", username);
                throw new ServiceException("对不起,您的账号:" + username + " 已被删除");
            } else if (UserStatus.DISABLE.getCode().equals(user.getStatus())) {
                log.info("登录用户:{} 已被停用.", username);
                throw new ServiceException("对不起,您的账号:" + username + " 已停用");
            } else {
                passwordService.validate(user);
                return createLoginUser(user);
            }
        }
        //都为空进行输出
        else {
            log.info("登录用户:{} 不存在.", user.getUserName());
            throw new ServiceException("登录用户:" + user.getUserName() + " 不存在");
        }

在这里,我们可以清楚的看到

		//培训信息用户
       RancoUser rancoUser = userService.selectUserByUserNameRanco(username);
       //服务信息用户
       SysUser server = userService.selectUserByUserNameServer(username);
       //客户信息用户
       SysUser customer = userService.selectUserByUserNameCustomer(username);
       //知识库用户
       SysUser knowledage = userService.selectUserByUserNameKnowledage(username);

这四个每一次都会进行一次换库查询,注意,这里是换库,不是切换数据表

21:03:01.376 [pool-2-thread-2] INFO  c.r.f.d.DynamicDataSourceContextHolder - [setDataSourceType,26] - 切换到SLAVE3数据源
21:03:01.377 [pool-2-thread-3] INFO  c.r.f.d.DynamicDataSourceContextHolder - [setDataSourceType,26] - 切换到SLAVE2数据源
21:03:01.376 [pool-2-thread-1] INFO  c.r.f.d.DynamicDataSourceContextHolder - [setDataSourceType,26] - 切换到SLAVE1数据源

所以呢,是一个非常耗时的事情

四. 性能优化

对于代码进行分析,我们发现代码其实有很多的相似之处,所以我们对于代码进行如下优化

	    //辅助对象
        SysUser user = null;
        //服务信息用户
        SysUser server = null;
        //客户信息用户
        SysUser customer = null;
        //知识库用户
        SysUser knowledage = null;
        //创建一个包含3个线程的线程池
        ExecutorService executorService = Executors.newFixedThreadPool(3);

        //将三个任务封装成Callable对象
        Callable serverCallable = new Callable() {
            public SysUser call() throws Exception {
                return userService.selectUserByUserNameServer(username);
            }
        };

        Callable customerCallable = new Callable() {
            public SysUser call() throws Exception {
                return userService.selectUserByUserNameCustomer(username);
            }
        };

        Callable knowledageCallable = new Callable() {
            public SysUser call() throws Exception {
                return userService.selectUserByUserNameKnowledage(username);
            }
        };

        //提交Callable任务给线程池并获取Future对象
        Future<SysUser> serverFuture = executorService.submit(serverCallable);
        Future<SysUser> customerFuture = executorService.submit(customerCallable);
        Future<SysUser> knowledageFuture = executorService.submit(knowledageCallable);

        //获取任务执行结果
        try {
            server = serverFuture.get();
            customer = customerFuture.get();
            knowledage = knowledageFuture.get();
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        } catch (ExecutionException e) {
            throw new RuntimeException(e);
        }

        //关闭线程池
        executorService.shutdown();

        //判断那个不为空辅助对象为那个
        user = StringUtils.isNotNull(server) ? server : StringUtils.isNotNull(customer) ? customer : StringUtils.isNotNull(knowledage) ? knowledage : null;
        //服务,客户,知识三大模块的用户鉴权
        if (StringUtils.isNotNull(user)) {
            if (UserStatus.DELETED.getCode().equals(user.getDelFlag())) {
                log.info("登录用户:{} 已被删除.", username);
                throw new ServiceException("对不起,您的账号:" + username + " 已被删除");
            } else if (UserStatus.DISABLE.getCode().equals(user.getStatus())) {
                log.info("登录用户:{} 已被停用.", username);
                throw new ServiceException("对不起,您的账号:" + username + " 已停用");
            } else {
                passwordService.validate(user);
                return createLoginUser(user);
            }
        }
        //都为空进行输出
        else {
            log.info("登录用户:{} 不存在.", user.getUserName());
            throw new ServiceException("登录用户:" + user.getUserName() + " 不存在");
        }

代码解读

上述代码实现了一个多线程的登录校验功能,主要流程如下:

  1. 定义了四个辅助对象user、server、customer、knowledage,分别表示服务信息用户、客户信息用户、知识库用户。
  2. 创建一个包含3个线程的线程池executorService。
  3. 将三个任务封装成Callable对象,分别对应服务信息用户的查询、客户信息用户的查询和知识库用户的查询操作。
  4. 提交Callable任务给线程池并获取Future对象。
  5. 通过调用Future对象的get()方法获取任务执行结果,将结果赋值给对应的辅助对象user。
  6. 根据辅助对象user的不同值进行不同的处理,如果存在服务信息用户的辅助对象则进行服务鉴权,如果存在客户信息用户的辅助对象则进行客户鉴权,否则进行知识库鉴权。
  7. 如果存在有效的用户辅助对象,则进行密码校验并返回登录成功的用户;否则抛出异常提示用户不存在或已被停用。
  8. 最后输出登录失败的信息。

需要注意的是,在多线程环境下,对共享变量的操作需要考虑线程安全性。对于本例中的辅助对象user,可以使用ConcurrentHashMap来代替传统的Java集合类实现,以确保多个线程同时对其进行访问时的线程安全性。

五. 场景抉择

  1. 多线程:适用于需要同时处理多个任务并且这些任务之间存在一定的关联性,例如网络编程、UI编程等。在网络编程中,每个客户端连接可以对应一个线程,处理该客户端的请求,加速服务处理的效率;在UI编程中,需要有一个主线程负责事件响应和界面更新,而耗时的操作则需要放到其他线程中执行,以保证用户体验。

  2. 多进程:适用于需要不同进程之间进行资源隔离,并能充分利用系统资源的场景,例如操作系统管理、大规模数据处理等。在操作系统管理中,每个程序可以独立运行在自己的进程中,避免了进程间互相干扰和占用资源的问题;在大规模数据处理中,可以通过将大数据分成若干个小块,在不同进程中并行处理,提高数据处理的效率。

  3. 异步:适用于需要提高程序并发度,减少等待时间的场景,例如网络通信、IO操作等。在网络通信中,如果采用同步阻塞方式,一个请求的响应需要等待服务端完成业务才能返回,会阻塞客户端的主线程,影响其它任务的执行;而异步方式,程序可以在等待的同时继续执行后续任务,当服务端响应时再通过回调等方式进行处理,提高了程序的并发度和效率。

  4. 并发:适用于需要同时执行多个任务的场景,例如服务器编程、数据库访问等。在服务器编程中,可以通过支持多线程或者多进程来实现并发处理客户请求;在数据库访问中,多个客户端可以同时访问数据库,避免了因为单一的访问入口而导致的性能瓶颈等问题。

总之,不同的技术手段都有其对应的使用场景,开发者需要根据具体业务需求选择合适的技术方案。

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

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

相关文章

PyQt5实现父窗口内点击按钮显示子窗口(窗口嵌套功能)

摘要&#xff1a;在软件中&#xff0c;常会有点击某个按钮&#xff0c;显示一个新的子界面的需求&#xff0c;本文介绍如何在PyQt5中实现这一功能&#xff0c;主要涉及知识点是“信号与槽函数的自动绑定”。 程序说明&#xff1a; 1.开发环境&#xff1a;win10系统&#xff0c…

如何在 Linux 中创建非登录用户?

在 Linux 系统中&#xff0c;用户账户的管理是一个重要的任务。除了常规的登录用户&#xff0c;有时候我们需要创建一些非登录用户&#xff0c;这些用户通常用于运行服务、执行特定任务或限制访问权限。 本文将详细介绍如何在 Linux 中创建非登录用户&#xff0c;并提供一些相关…

基于Redis的Java分布式锁,接口并发处理,并发方案

Redis的分布式锁很多人都知道&#xff0c;比如使用Jedis的setNx、incr等方法都可以实现分布式锁的功能&#xff0c;但是Jedis需要自己管理连接池&#xff0c;就稍微麻烦一点。 今天介绍的是使用RedisTemplate切面编程自定义注解SPEL来实现分布式锁的功能&#xff0c;封装完成后…

(电脑硬件)台式机主板音频端口功能详解

当你想给你的主机插上音响或者耳机时&#xff0c;你会发现主板上有6个接口&#xff0c;同样都是3.5mm接口&#xff0c;你知道该插哪个吗&#xff1f; 一般情况下&#xff0c;后置输入输出端口面板中&#xff0c;大多数的主板音频部分是彩色的。这一类颜色跟功能基本是固定的。当…

代码随想录训练营Day53| 1143.最长公共子序列 1035.不相交的线 53. 最大子序和 动态规划

目录 学习目标 学习内容 1143.最长公共子序列 1035.不相交的线 53. 最大子序和 动态规划 学习目标 1143.最长公共子序列 1035.不相交的线 53. 最大子序和 动态规划 学习内容 1143.最长公共子序列 1143. 最长公共子序列 - 力扣&#xff08;LeetCode&#xff09;ht…

通过ChatGPT跟MetaHuman对话,Android/iOS兼容

一、申请ChatGPT的API-KEY 1.通过 openAI官网申请API-KEY 2.参考使用腾讯云函数一分钟搭建 OpenAI 免翻墙代理搭建openAI免翻墙代理 3.通过Postman测试一下openAI函数是否可被调用,传入BearerToken和Body参数,ChatGPT即可返回应答数据 二、启用必要的插件 1.启用文字转语…

文本三剑客-Sed

sed工作原理 sed的特点&#xff1a; sed基本语法 模式空间中的编辑操作---地址定界 常用编辑命令 增添&#xff08;a&#xff09; 追加&#xff08;i&#xff09; 删除&#xff08;d&#xff09; 读入文件&#xff08;r&#xff09; 固定长度替换&#xff08;y&#xff0…

Segment Anything专题论文和代码汇总

文章目录 2023Scaling-up Remote Sensing Segmentation Dataset with Segment Anything ModelPersonalize Segment Anything Model with One ShotSegment Anything in Medical ImagesMatcher: Segment Anything with One Shot Using All-Purpose Feature MatchingCustomized Se…

程序员困局:去大城市进大厂却买不了房,回老家又没有高薪工作…

对于在外打拼的程序员来说&#xff0c;难的是进大厂&#xff0c;而不是买不起房。 进大厂的程序员&#xff0c;能不能买得起房&#xff1f; 进大厂的程序员的薪资&#xff0c;还是相当可观的。以阿里P6为例&#xff0c;年薪50万&#xff0c;到手40万左右&#xff0c;刨去10万…

C++小知识点(auto关键字)

&#x1f339;作者:云小逸 &#x1f4dd;个人主页:云小逸的主页 &#x1f4dd;Github:云小逸的Github &#x1f91f;motto:要敢于一个人默默的面对自己&#xff0c;强大自己才是核心。不要等到什么都没有了&#xff0c;才下定决心去做。种一颗树&#xff0c;最好的时间是十年前…

5月跳槽会有风险,不跳也会有?

今天讲讲跳槽。 说实话跳槽是为了寻求更好的发展&#xff0c;但在跳槽前我们也不能确定下家就是更好的归宿&#xff0c;这就更加需要我们审慎地去对待&#xff0c;不能盲目跳槽。 其次&#xff0c;我们离职和跳槽&#xff0c;其中的原因很大一部分是目前薪资不符合预期。 那…

【面试篇】SpringIoC、AOP、MVC面试实战

version&#xff1a;1.0 文章目录 SpringSpring基础 / IoC&#x1f64e;‍♂️面试官&#xff1a;举例Spring的模块&#xff1f;&#x1f64e;‍♂️面试官&#xff1a;Spring、SpringMVC、Spring Boot关系&#xff1f;&#x1f64e;‍♂️面试官&#xff1a;说说对SpringIoC的…

人工智能值不值得学习?人工智能就业方向及前景

人工智能值不值得学习? 一、人工智能值得学吗&#xff1f; 很多同学想要知道人工智能值得学吗&#xff1f;小编认为是值得的&#xff0c;具体原因有以下两点&#xff1a; 1、人工智能专业前景好&#xff0c;但人才紧缺 根据人工智能行业的专家预计&#xff0c;到2020年&am…

探索【Stable-Diffusion WEBUI】的图片超分辨插件:StableSR

文章目录 &#xff08;零&#xff09;前言&#xff08;一&#xff09;图片放大&#xff08;二&#xff09;图片超分辨率放大脚本插件&#xff08;StableSR&#xff09;&#xff08;2.1&#xff09;下载组件&#xff08;2.2&#xff09;使用&#xff08;2.3&#xff09;实例对比…

网上学影视后期靠谱吗 影视后期剪辑需要学什么

影视后期如果有人手把手当面教的话&#xff0c;当然是最好的。但很多人都没有这么好的条件&#xff0c;实际上&#xff0c;网上也有很多教程可以学习利用。不过&#xff0c;小伙伴们可能会有疑问&#xff0c;网上学影视后期靠谱吗&#xff0c;影视后期剪辑需要学什么&#xff1…

Java API 基础

Java API 基础 一、相关知识学习 Java程序员在开发Java程序时&#xff0c;只需要安装有JDK&#xff0c;就可以在程序中使用import关键字导入Java API 中指定的包并在自己的程序中使用这些包中定义的各种类和接口。 1、 Java API 包 Java API 包 说明 java.accessibility 接…

【服务器】支付宝SDK接口调试

​ 文章目录 1.测试环境2.本地配置3. 内网穿透3.1 下载安装cpolar内网穿透3.2 创建隧道 4. 测试公网访问5. 配置固定二级子域名5.1 保留一个二级子域名5.2 配置二级子域名 6. 使用固定二级子域名进行访问 转发自cpolar内网穿透的文章&#xff1a;Java支付宝沙箱环境支付&#…

物联网通信协议-MQTT及使用python实现

MQTT概念及其原理 简述 MQTT(Message Queuing Telemetry Transport&#xff0c;消息队列遥测传输协议&#xff09;&#xff0c;是一种基于发布/订阅&#xff08;publish/subscribe)模式的"轻量 级"通讯协议&#xff0c;该协议构建于TCP/IP协议上&#xff0c;由IBM在…

御剑WEB指纹识别系统教程,图文教程(超详细)

「作者简介」&#xff1a;CSDN top100、阿里云博客专家、华为云享专家、网络安全领域优质创作者 「推荐专栏」&#xff1a;对网络安全感兴趣的小伙伴可以关注专栏《网络安全入门到精通》 御剑WEB指纹识别 一、基本使用二、内置字典三、自定义字典四、扫描原理 御剑WEB指纹识别系…

基于DDSRF正负序分离方法的不平衡电网PQ控制策略_平衡电流控制

0.前言 对于并网逆变器而言&#xff0c;电网会存在不平衡的情况。在这种情况下&#xff0c;不平衡的电网电压可以分解成为正序、负序和零序分量。并网逆变器通常期望能够实现单位功率因数并网&#xff0c;向电网注入对称的正弦电流&#xff0c;所以此时的微电网逆变器控制策略显…