多线程的实际应用场景,有返回值的处理
业务场景
我们公司做的是加油业务,用户可以在app上通过当前位置和目标位置来查询地图路线以及路线途径的所有加油站,路线查询会调用高德地图提供的接口,途径油站则根据返回的路线信息进行查询,所以当用户输入起始位置和目标位置点击查询后会做以下几步操作:调用高德地图接口获取路线根据高德地图返回的路线信息去查询途径的所有油站问题点
公司发展很快,入驻平台的油站很多,当用户输入的起始地和目标地距离很远时,那么途径油站的数量会很大,单独采取普通查询库的方式会很耗时,并且途径油站的数据必须是实时的,所以无法使用缓存来提高接口响应效率。第一步是调用高德地图API,也会存在一定的延迟。那么我们应该怎么优化呢?
为了降低接口耗时,提高用户体验,我们需要对接口实现进行优化,调用高德API我们无法优化,所以只能优化查询途径油站这部分。优化思路
当油站过多时,一次查询会很耗时,所以我们可以考虑分批多线程并发的去查询,将一段很长的路线按照路径长度分成若干个条件,比如一段路径长达800km,我们可以将这800km的查询参数拆分成若干个距离较小的参数集合(ps:举例方便大家理解,实际路径规划查询都是根据经纬度、距离等多重参数进行查询的)。比如,{〔0,50〕,〔50,100〕,〔100,150〕。。。。。〔750,800〕},这时我们开启多个线程去并发的根据新的查询条件去查询,最后将结果拼接封装返回,从而达到降低查询时间的目的。
虽然思路很容易理解,但是实现起来有两个需要注意的地方,我列出来看看大家有没有考虑到。根据业务场景,这里不是单纯的异步查询就可以的,而是需要所有的线程都执行完后并且组合查询结果后进行返回,所以这里需要进行同步控制。这里我们使用jdk提供的CountDownLatch同步组件实现。线程内操作需要有返回值,使用Callable接口以及FutureTask搭配实现。具体实现
1。通常来说,我们定义线程需要实现Runnable接口,但是对于需要返回值的线程,就需要线程实现Callable接口了。ComponentSlf4jScope(protoType)这里需要注意Spring默认注入的Bean都是单例的,当前业务场景下肯定需要多个线程去执行查询操作,所以这里声明组件为protoType模式publicclassPathPlanTaskimplementsCallableListObject{查询参数privatePathPlanPartQuerypathPlanPartQuery;privateCountDownLatchcountDownLatch;OverridepublicListObjectcall()throwsException{try{TODO业务查询ListObjectresultqueryList(pathPlanPartQuery);返回结果returnresult;}catch(Exceptione){错误日志打印log。error(queryPathByGasstationerror!);}finally{类似i的操作,当减到0的时候,countDownLatch。await()就会放行,否则会一直阻塞。countDownLatch。countDown();}}publicvoidsetPathPlanPartQuery(PathPlanPartQuerypathPlanPartQuery){this。pathPlanPartQuerypathPlanPartQuery;}publicvoidsetCountDownLatch(CountDownLatchcountDownLatch){this。countDownLatchcountDownLatch;}privateListObjectqueryList(PathPlanPartQuerypathPlanPartQuery){TODO具体查询逻辑,这里省略returnLists。newArrayList();}}复制代码
2。Callable通常和FutureTask搭配使用,通过FutureTask的get方法获取到线程的返回值。通常定义为工具类进行获取privatestaticfinalThreadPoolExecutorpoolExecutornewThreadPoolExecutor(8,20,1000,TimeUnit。SECONDS,newArrayBlockingQueue(50),newThreadPoolExecutor。AbortPolicy());业务代码privateListObjectqueryGasInfoBaseDtoList(ListPathPlanQueryParamqueryParamList){longstMillsSystem。currentTimeMillis();定义线程池来进行多线程的管理,通过Util获取静态的线程池定义countDownLatch,构造函数传递参数集合的size,该集合具体参数可以参考上面举的例子,{〔0,50〕,〔50,100〕,〔100,150〕。。。〔750,800〕}CountDownLatchcountDownLatchnewCountDownLatch(queryParamList。size());批量查询,定义FutureTask集合ListFutureTaskListGasInfoBaseResponseDtofutureTaskListLists。newArrayList();try{遍历查询参数集合for(PathPlanQueryParamqueryParam:queryParamList){这里使用getBean方式获取。PathPlanTaskpathPlanTaskApplicationContextProvider。getBean(pathPlanTask,PathPlanTask。class);设置countDownLatchpathPlanTask。setCountDown(countDownLatch);获取查询参数PathPlanPartQuerypathPlanPartQuerygetPathPlanPartQuery(queryParam);pathPlanTask。setPathPlanPartQuery(pathPlanPartQuery);定义FutureTask,将定义好的Callable实现类作为构造参数FutureTaskListGasInfoBaseResponseDtofutureTasknewFutureTask(pathPlanTask);交给线程池去执行poolExecutor。submit(futureTask);添加futureTask集合futureTaskList。add(futureTask);}这里会一直进行阻塞,直到countDownLatch。countDown()方法将创建时传递的size参数减为0后放行。这块可以保证多个线程全部执行完后进行最终返回。countDownLatch。await();多个线程执行完后我们拼接最终结果ListObjectgasInfoDtoListLists。newArrayList();for(FutureTaskListObjectfutureTask:futureTaskList){通过futrueTask的get方法获取返回值,当线程还在执行未返回时执行futureTask。get()会被阻塞ListObjectbaseResponseDtoListfutureTask。get();if(CollectionUtils。isNotEmpty(baseResponseDtoList)){gasInfoDtoList。addAll(baseResponseDtoList);}}returngasInfoDtoList;}catch(Exceptione){log。error(queryGasInfoBaseDtoListerr,e);}finally{log。info(queryGasInfoBaseDtoListrequestId:{},batchTimes:{},cost:{},pointRequestOld。getRequestId(),pointRequestOld。getBatchTimes(),System。currentTimeMillis()stMills);}returnnull;}复制代码总结
以上是我在工作中遇到的多线程实际应用的场景,总结下来就是通过线程池开启多个Callable线程去分批查询数据,引入CountDownLatch组件来控制查询结束时机,而后利用FutureTask的get方法获取最终结果拼装返回。
华为的二公主,恨铁不成钢大家好,我是维小维。说真的,2021年真的见证历史。我们既见证了年初还在播放《小舍得》深感需痛追鸡娃,年中就发现不用鸡了,教培机构差不多都死光光又见证了年初才……
现在说华为mate9不卡顿的人,是真心话吗?编者按:确实真心话,因为华为mate9这款手机,我自己也在使用。并不是说只有在屏幕上面滑动几下就算是不卡顿,在我的理解中,至少打开微信,以及淘宝,和王者荣耀是流畅的,确实华为m……
为追爱豆,三位忠爱粉如此神操作,看完你会跪为追爱豆,粉丝如此神操作,看完你会跪!第三名:陈冠希粉丝一名女粉丝梦想着能与他拍一部电影,粉丝的妈妈知道女儿的想法后,当即拍板搞了一个剧组,让女儿编剧制片作女主,豪……
4K超高清海思芯片前后录制AKASOKingSlim行车记录如今基本上家家户户都拥有自己的小汽车,日常通勤,短途旅行都是不可或缺的存在;然而随着汽车的日益增多,有时不免会发生意外的交通事故,以及有些图谋不轨的人碰瓷,这个时候你可能有理也……
iphone苹果经过双十一收货价11128黑4100白4135绿4040紫408012128G黑4855白4835红4850蓝4900绿4810紫479513128G黑5515白5560粉5570……
调音实践中的一些技巧阜新声艺视听人耳对音色的感觉比较灵敏,可以直接判断声音是否逼真。音色在调音方面不可不谓之重要,稍对音色处理不好,就会造成声音单调,枯燥乏味,更有甚者,造成乐器或者演唱产生严重的失真。今天就……
朗新转债中签号出炉朗新转债本次发行总额8。00亿元,申购人数838万,中签率2。2,平均49户才能中一签,网上发行占本次发行总量的22。74,最终向原股东共优先配售朗新转债共6,180,995张……
人到中年,我决定开始学数据分析作者:Leo本文为心中有数CDA征文作品过往的经历比较复杂,感觉没有必要在心事重重的讲一遍,先谈谈为什么学习数据分析。人到中年,深刻的感受到社会对于大龄人士的……
新疆首个共享新能源汽车换电站投用20秒更换动力电池9月1日,新能源汽车车主扫码缴费进入换电站。城投奥动共享新能源汽车换电站位于昌吉市森林公园静苑巷,由昌吉市城投热力公司与成都奥动公司合作建设,于8月25日投用,这是我区首个共享……
双5G待机,玩游戏顺畅,性价比超高得手机随着5G网络的普及,越来越多的人开始使用5G网络的手机,红米10X手机有双5G待机,可以覆盖更多的网络信号,给你带来强烈的信号的同时让手机以更低的功耗运行。当你用红米10X手机……
win7系统电脑磁盘碎片整理的使用方法大部分的深度技术的用户,都清楚在win732系统中电脑运行时间长了,会产生各种磁盘碎片,如果电脑磁盘碎片不整理的话,容易导致操作系统变卡顿。所以我们要经常进行磁盘碎片整理,这样……
小米室外摄像机电池版拆解硬件方案叫绝,可视化门铃的孪生兄弟PauseUnmuteCurrentTime1:00Duration10:41Loaded:11。57RemainingTimePictureinPictureFullscre……
世界在发展,生活在变化小学品德教案教学目标:了解机器生产与手工生产的区别。初步体验和认识工业化对于人类的重大意义。学习运用批判性思维认识工业化,认识中国的近代史。教学过程:导入。请……
永生的眼睛教学反思范文《永生的眼睛》是四年级下册第六单元热爱生命单元的一篇文章,展示了我的父辈为了他人能够更好地生活,下面是小编收集整理的永生的眼睛教学反思,欢迎阅读参考!永生的眼睛教学反思1……
浅谈编制数学教学设计应遵循的理念摘要:新课程理念下数学教学设计要根据学生特点定位教学目标,始终体现每一个教学的子目标;教学情境的创设要恰如其分,激发学生的求知欲;巧妙搭建高效的学生质疑的平台,挖掘教材内容中质……
山青青教学设计【内容简析】这是一首短小优美的诗歌。全文只有三句话,第一句写山、水、鸟;第二句写树、草、花;第三句写苗、田、春风春雨。诗画相配,内容生动,更富有想像力,读起来琅琅上口,饶……
无机非金属材料的主角硅2教学设计知识与技能:了解硅酸盐的主要用途和组成。了解硅的物理性质和重要用途。掌握硅酸钠的化学性质。过程与方法:通过自主探究,培养学生自主学习的能力情……
约数和倍数的意义数学教案一、教法建议【抛砖引玉】通过本单元的教学要使学生掌握整除、约数、倍数、质数、合数、质因数、公约数、最大公约数、公倍数、最小公倍数等概念;知道有关概念之间的联系和区别……
彩球的设计教学反思本节课的成功之处有以下几个方面:首先是课前布置的课室激起了学生强烈的学习欲望,学生都迫不及待地想要立刻动手去制作了。其次是讲授过程中没有像以往那样先讲步骤,然后学生制作。而是让……
福楼拜家的星期天的教案设计【教学目标】、速读课文,体会人物形象,培养学生的自主学习能力。、学习本文抓住外貌、性格特征写人的描写方法。、揣摩比较不同人物不同的描写方法。、感受当时欧……
中学生用词语造句1、扯皮:不和你扯皮了,我们说正事儿。2、补补课:病了几天,还不补补课学习就跟不上了。3、来龙去脉:这件事很麻烦,我花了三天时间才搞清楚它的来龙去脉,但已是头昏眼花……
小班艺术小小粉刷匠活动目标:1、在说色过程中,学习用刷子来回涂画的本领;2、享受共同游戏,共同作画的乐趣;3、在活动中分辨红、黄、蓝三种颜料,鼓励幼儿大胆用解角。活动准备:幼儿人手一把刷子和一只……
有关于济南的冬天教学教案设计济南的冬天(第一课时)教学目标1。熟悉课文内容,理清文章层次结构和主要内容。2。理解通过比较的写法突出事物的特点。3。潜移默化热爱祖国河山的感情。……
红花幼儿园卫生消毒工作细则【个人卫生】幼儿饭前便后要用肥皂液洗手,饭后漱口擦嘴。每周剪一次指甲。工作人员要保持仪表整洁,不留长指甲。【环境卫生】1。保持室内外清洁卫生、物品要整齐。每日……