物联网流量卡骗局_纯流量卡骗局

物联网流量卡骗局_纯流量卡骗局冰河用多线程优化了亿级流量电商业务下的海量数据校对系统,性能直接提升了200%,这次将整个优化过程分享给大家,全程干货,建议收藏!!

大家好,又见面了,我是你们的朋友全栈君。如果您正在找激活码,请点击查看最新教程,关注关注公众号 “全栈程序员社区” 获取激活教程,可能之前旧版本教程已经失效.最新Idea2022.1教程亲测有效,一键激活。

Jetbrains全系列IDE使用 1年只要46元 售后保障 童叟无欺

大家好,我是冰河~~

最近不少运营同事找到我说:咱们的数据校对系统越来越慢了,要过很久才会显示出校对结果,你能不能快速优化一下呢?我:好的,我先了解下业务啊。

在这里插入图片描述
注:全程干货,文章对你有点帮助的话,小伙伴们点赞,收藏,评论,分享,走起~~

优化背景

由于这个数据校对系统最初不是我开发的,我了解了下数据校对系统的业务,整体来说,数据校对系统的业务还是比较简单的。用户通过商城提交订单后,会在订单微服务中生成订单信息,保存在订单数据库中。

订单微服务会调用库存微服务的接口,扣减商品的库存数量,并且会将每笔订单扣减库存的记录保存在库存数据库中。为了防止用户提交订单后没有扣减库存,或者重复扣减库存,数据校对系统每天会校验订单中提交的商品数量与扣减的库存数量是否一致,并且会将校对的结果信息保存到数据校对信息表中。

数据校对系统的总体流程为:先查询订单记录,然后在查询库存的扣减记录,然后对比订单和库存扣减记录,然后将校对的结果信息保存到数据校对信息表中,整体流程如下所示。

在这里插入图片描述

为了能够让大家更好的了解数据校对系统对于订单和库存的校对业务,我将代码精简了下,核心业务逻辑代码如下所示。

//检测是否存在未对账订单
checkOrders = checkOrders();
while(checkOrders != null){ 
   
    //查询未校对的订单信息
    hasNoOrders = getHasNoOrders();
    //查询未校对的库存记录
    hasNoStock = getHasNoStock();
    //校对数据并返回结果
    checkResult = checkData(hasNoOrders, hasNoStock);
    //将结果信息保存到数据校对信息表中
    saveCheckResult(checkResult);
    //检测是否存在未对账订单
    checkOrders = checkOrders();
}

好了,上述就是系统优化的背景,想必看到这里,很多小伙伴应该知道问题出在哪里了。我们继续往下看。

问题分析

虽然很多小伙伴应该已经知道系统性能低下的问题所在了,这里,我们就一起详细分析下校对系统性能低下的原因。

既然运营的同事说数据校对系统越来越慢了,我们首先要做的就是找到系统的性能瓶颈所在。据了解,目前的数据对账系统,由于订单记录和库存扣减记录数据量巨大,所以查询未校对的订单信息的方法getHasNoOrders()和查询为校对的库存记录的方法getHasNoStock()相对来说比较慢。并且在数据校对系统中,校对订单和库存记录的方法是单线程执行的,我们可以简单画一个时间抽线图,如下所示。

在这里插入图片描述

由图可以看出,以单线程的方式getHasNoOrders()方法和getHasNoStock()方法耗费了大量的时间,这两个方法本身在逻辑上就是两个独立的方法,并且这两个方法没有先后的执行的顺序依赖。那这两个方法能不能并行执行呢?很显然是可以的。那我们把getHasNoOrders()方法和getHasNoStock()方法分别放到两个不同的线程中,优化下系统的性能,整体流程如下所示。
在这里插入图片描述

优化后,我们将getHasNoOrders()方法放到线程1中执行,getHasNoStock()方法放到线程2中执行,checkData()方法和saveCheckResult()方法发放到线程3中执行,优化后的系统性能相比优化前的系统性能几乎提升了一倍,优化效果相对来说还是比较明显的。

说到这里,大家应该应该知道具体怎么优化了吧?好,我们继续往下看!

解决方案

解决问题的思路有了,接下来,我们看看如何使用代码实现我们上面分析的解决问题的思路。这里,我们可以分别开启两个线程执行getHasNoOrders()方法和getHasNoStock()方法,在主线程中执行checkData()方法和saveCheckResult()方法。这里需要注意的是:主线程需要等待两个子线程执行完毕之后再执行checkData()方法和saveCheckResult()方法。

为了实现这个功能,我们可以使用Thread类中join()方法,有关Thread类中join()方法的具体说明,小伙伴们可以参见《高并发之——朋友去面试竟然栽在了Thread类的源码上》一文。这里,具体的逻辑就是在主线程中调用两个子线程的join()方法实现阻塞等待,当两个子线程执行完毕退出时,调用两个子线程join()方法的主线程会被唤醒,从而执行主线程中的checkData()方法和saveCheckResult()方法。大体代码如下所示。

//检测是否存在未对账订单
checkOrders = checkOrders();
while(checkOrders != null){ 
   
    Thread t1 = new Thread(()->{ 
   
        //查询未校对的订单信息
        hasNoOrders = getHasNoOrders();
    });
    t1.start();
    Thread t2 = new Thread(()->{ 
   
       //查询未校对的库存记录
       hasNoStock = getHasNoStock();
    });
     t2.start();
    //阻塞主线程,等待线程t1和线程t2执行完毕
    t1.join();
    t2.join();
    //校对数据并返回结果
    checkResult = checkData(hasNoOrders, hasNoStock);
    //将结果信息保存到数据校对信息表中
    saveCheckResult(checkResult);
    //检测是否存在未对账订单
    checkOrders = checkOrders();
}

至此,我们基本上能够解决问题了。但是,还有没有进一步优化的空间呢?我们进一步往下看。

进一步优化

通过上面对系统优化,基本能够达成我们的优化目标,但是上面的解决方案存在着不足的地方,那就是在while循环里每次都要新建两个线程分别执行getHasNoOrders()方法和getHasNoStock()方法,了解Java多线程的小伙伴们应该都知道,在Java中创建线程可是个非常耗时的操作。所以,最好是能够将创建出来的线程反复使用。这里,估计很多小伙伴都会想到使用线程池,没错,我们可以使用线程池进一步优化上面的代码。

遇到新的问题

不过在使用线程池进一步优化时,我们会遇到一个问题,就是主线程如何等待子线程中的结果数据呢?说直白点就是:主线程如何知道子线程中的getHasNoOrders()方法和getHasNoStock()方法执行完了? 由于在之前的代码中我们是在主线程中调用子线程的join()方法等待子线程执行完毕,获取到子线程执行的结果后,继续执行主线程的逻辑。但是如果使用了线程池的话,线程池中的线程根本不会退出,此时,我们无法使用线程的join()方法等待线程执行完毕。

所以,主线程如何知道子线程中的getHasNoOrders()方法和getHasNoStock()方法执行完了? 这个问题就成了关键的突破点。这里,我们使用线程池进一步优化的代码如下所示。

//检测是否存在未对账订单
checkOrders = checkOrders();
//创建线程池
Executor executor =  Executors.newFixedThreadPool(2);
while(checkOrders != null){ 
   
    executor.execute(()->{ 
   
        //查询未校对的订单信息
        hasNoOrders = getHasNoOrders();
    });
    executor.execute(()->{ 
   
       //查询未校对的库存记录
       hasNoStock = getHasNoStock();
    });
    
    /**如何知道子线程中的getHasNoOrders()方法和getHasNoStock()方法执行完了成为关键**/
    
    //校对数据并返回结果
    checkResult = checkData(hasNoOrders, hasNoStock);
    //将结果信息保存到数据校对信息表中
    saveCheckResult(checkResult);
    //检测是否存在未对账订单
    checkOrders = checkOrders();
}

那么,如何解决这个问题呢?我们继续往下看。

新的解决方案

相信细心的小伙伴们能够看出,整个业务的场景就是:一个线程需要等待其他两个线程的逻辑执行完毕后再执行。在Java的并发类库中,为我们提供了一个能够在这种场景下使用的类库,那就是CountDownLatch类。如果对CountDownLatch类不了解的小伙伴可以参考《浅谈AQS中的CountDownLatch、Semaphore与CyclicBarrier》一文。

使用CountDownLatch类优化我们程序的具体做法就是:在程序的while()循环中首先创建一个CountDownLatch对象,计数器的值初始化为2。分别在hasNoOrders = getHasNoOrders();代码和hasNoStock = getHasNoStock();代码的后面调用latch.countDown()方法使得计数器的值分别减1。在主线程中调用latch.await()方法,等待计数器的值变为0,继续往下执行。这样,就能够完美解决我们遇到的问题了。优化后的代码如下所示。

//检测是否存在未对账订单
checkOrders = checkOrders();
//创建线程池
Executor executor =  Executors.newFixedThreadPool(2);
while(checkOrders != null){ 
   
    CountDownLatch latch = new CountDownLatch(2);
    executor.execute(()->{ 
   
        //查询未校对的订单信息
        hasNoOrders = getHasNoOrders();
        latch.countDown();
    });
    executor.execute(()->{ 
   
       //查询未校对的库存记录
       hasNoStock = getHasNoStock();
       latch.countDown();
    });
    
    //等待子线程的逻辑执行完毕
    latch.await();
 
    //校对数据并返回结果
    checkResult = checkData(hasNoOrders, hasNoStock);
    //将结果信息保存到数据校对信息表中
    saveCheckResult(checkResult);
    //检测是否存在未对账订单
    checkOrders = checkOrders();
}

至此,我们就完成了系统的优化工作。

总结与思考

这次系统性能的优化,主要是将单线程执行的数据校对业务,优化成使用多线程执行。在平时的工作过程中,我们需要认真思考,找到系统性能瓶颈所在,找出在逻辑上不相干,并且没有先后顺序的业务逻辑,将其放到不同的线程中执行,能够大大提供系统的性能。

这次,对于系统的优化,我们最终使用线程池来执行比较耗时的查询订单与查询库存记录的操作,并且在主线程中等待线程池中的线程逻辑执行完毕后再执行主线程的后续业务逻辑。这种场景,使用Java中提供的CountDownLatch类再合适不过了。这里,再强调一下:CountDownLatch主要的使用场景就是一个线程等待多个线程执行完毕后再执行。如下图所示。

在这里插入图片描述

这里,也进一步提醒了我们:如果想学好并发编程,熟练的掌握Java中提供的并发类库是我们必须要做到的。

最后,给大家一个思考题:其实,上面的代码不是最优的,你有更好的优化方法吗?欢迎各位小伙伴在文末留言讨论呀!

写在最后

如果你想进大厂,想升职加薪,或者对自己现有的工作比较迷茫,都可以私信我交流,希望我的一些经历能够帮助到大家~~

推荐阅读:

好了,今天就到这儿吧,小伙伴们点赞、收藏、评论,一键三连走起呀,我是冰河,我们下期见~~

物联网流量卡骗局_纯流量卡骗局

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/172030.html原文链接:https://javaforall.net

(0)
上一篇 2022年8月22日 下午1:16
下一篇 2022年8月22日 下午1:16


相关推荐

  • CSS-基础篇

    CSS-基础篇CSS 基础部分自学笔记内联样式也称行内或行间样式 是在 html 标签上添加 style 属性来实现的内部样式在 style style 标签内添加的样式 内部样式的代码可以复用 符合 W3C 标准 尽量让结构和样式分开处理外部样式 linkrel stylesheet href rel 指定资源跟页面的关系 href 资源的地址 importurl common css 这种方式有很多问题 不建议使用颜色表示法单词表示法 linkrel stylesheet href

    2026年3月16日
    3
  • TCPDF_tcpip详解套装共3册pdf

    TCPDF_tcpip详解套装共3册pdf简介这篇博客主要是记录tcpdf在使用中的一些要点和注意事项。

    2025年10月9日
    3
  • hbuilder快捷键汇总

    hbuilder快捷键汇总为了脱离鼠标 还是需要了解下快捷键的 下面全手敲 累死我了 运行 ctrl r nbsp nbsp nbsp nbsp nbsp 常用激活代码助手 alt 删除当前行 ctrl d nbsp nbsp nbsp 常用删除前一词 ctrl backspace nbsp nbsp nbsp 常用删除当前标签 ctrl shift t 安全重命名对象 ctrl f2 合并下一行 ctrl alt j 整理代码格式 ctrl shift f nbsp nbsp nbsp 常用向下移动行 ctrl 向下键 nbsp nbsp nbsp 常用开

    2026年3月18日
    1
  • 五年了,我在 CSDN 的两个一百万。

    五年了,我在 CSDN 的两个一百万。在路上

    2026年2月11日
    5
  • endnote修改参考文献格式为方括号(参考文献)

    Endnote修改参考文献格式1将参考文献除编号外的内容设置左对齐:1)菜单栏Edit-Outputstyles-选择一个要更改的参考文献格式进行更改2)弹出页面内选中Bibliography下的Layout![右上角Incertfield位置添加tab,右下角HangingIndent位置选择Allparagraphy]3)在word中endnote下点击箭头处更改缩进大小最终结果如图…

    2022年4月10日
    3.8K
  • 把数据库中的静态图片遍历在前端页面上[通俗易懂]

    把数据库中的静态图片遍历在前端页面上[通俗易懂]把数据库中的静态图片遍历在前端页面上先上数据库中数据其次是后端的接口(把所有数据封装到List集合中发送过去):@RequestMapping(value=”/scenicSpots”,method=RequestMethod.GET)publicActionResultfindScenicSpots(){List<ScenicSpots>scenicSpots=scenicSpotService.QueryScenicSpots();

    2022年5月10日
    50

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号