当前位置: 首页 > 后端技术 > Java

这4个统计代码,执行起来很费时间,也够优雅!

时间:2023-04-01 14:56:48 Java

送你以下java学习资料。有一种方法可以在文章末尾获取它们。今天给大家分享一下如何统计代码中的接口耗时,最优雅,性能最高。接下来介绍4种统计方法。如果你有更好的方法欢迎到文末留言区。交流1.前言代码耗时统计是日常开发中很常见的需求,尤其是需要查找代码性能瓶颈的时候。也可能受限于Java的语言特性。总觉得代码不够优雅,大量耗时的统计代码干扰了业务逻辑。尤其是在开发功能的时候,感觉刚开发的代码很清爽优雅,但是加了很多辅助代码之后,整个代码就变得臃肿了,感觉很不舒服,所以一直在想是不是写这块更优雅。今天,本文将尝试讨论“代码耗时统计”这一块。正文开始前先说一下前提,“代码耗时统计”不是某个方法的耗时,而是任意代码段之间的耗时。这个代码段可能是一个方法中的几行代码,也可能是从这个方法的某一行到另一个被调用方法的某一行,所以这个需求是无法通过AOP来实现的。2.常规方法2.1时差统计这种方法是最简单的方法,记录开始时间,然后记录结束时间,计算时差。公共类TimeDiffTest{publicstaticvoidmain(String[]args)throwsInterruptedException{finallongstartMs=TimeUtils.nowMs();时间TimeUnit.SECONDS.sleep(5);//模拟业务代码"""System.printnCostl:"+TimeUtils.diffMs(startMs));}}/*输出:timeCost:5005*/``````publicclassTimeUtils{/***@returncurrentmilliseconds*/publicstaticlongnowMs(){returnSystem.currentTimeMillis();/***当前毫秒和开始毫秒之间的差异*@paramstartMillis现在开始纳秒*@return时间差*/publicstaticlongdiffMs(longstartMillis){returnMillsdiffMs,startMillis(startMillis);}}这种方法的优点是实现简单,易于理解;缺点是对代码的侵入性比较大,看起来比较傻,不够优雅。2.2StopWatch第二种方式是指StopWatch。StopWatch通常用于统计代码耗时。每个框架和通用包都有自己的实现。publicclassTraceWatchTest{publicstaticvoidmain(String[]args)throwsInterruptedException{TraceWatchtraceWatch=newTraceWatch();traceWatch.start("function1");TimeUnit.SECONDS.sleep(1);//模拟业务代码traceWatch.stop();traceWatch.start("function2");TimeUnit.SECONDS.sleep(1);//模拟业务代码traceWatch.stop();traceWatch.record("function1",1);//直接记录日志时间System.out.println(JSON.toJSONString(traceWatch.getTaskMap()));}}/*输出:{"function2":[{"data":1000,"taskName":"function2"}],"function1":[{"data":1000,"taskName":"function1"},{"data":1,"taskName":"function1"}]}*/``````publicclassTraceWatch{/**当前任务的开始时间。*/privatelongstartMs;/**当前任务的名称。*/@NullableprivateStringcurrentTaskName;@GetterprivatefinalMap>taskMap=newHashMap<>();/***开始时间差异类型索引记录,如果需要终止,请调用{@link#stop()}***@paramtaskName指标名称*/publicvoidstart(StringtaskName)throwsIllegalStateException{if(this.currentTaskName!=null){t"TractionStateegCanthrownewI:它已经在运行");}}this.currentTaskName=taskName;this.startMs=TimeUtils.nowMs();}/***终止时差类型指标记录,请确保调用了IEx*/pllegpublicStaterowstop({if(this.currentTaskName==null){thrownewIllegalStateException("Can'tstopTraceWatch:它没有运行");}longlastTime=TimeUtils.nowMs()-this.startMs;TaskInfoinfo=newTaskInfo(this.currentTaskName,lastTime);this.taskMap.computeIfAbsent(this.currentTaskName,e->newLinkedList<>()).add(info);this.currentTaskName=null;}/***直接记录指标数据,不限时差类型*@paramtaskName指标名称*@paramdata指标数据*/publicvoidrecord(StringtaskName,Objectdata){TaskInfoinfo=newTaskInfo(taskName,data);this.taskMap.computeIfAbsent(taskName,e->newLinkedList<>()).add(info);}@GetterAllArgsConstructorpublicstaticfinalclassTaskInfo{privatefinalStringtaskName;私有最终对象数据;我按照org.springframework.util.StopWatch的实现编写了TraceWatch类。该方法提供了两种统计耗时的方法:调用Start(name)和Stop()方法进行耗时统计。通过调用Record(name,timeCost)方法,可以直接记录耗时信息。这种方法和“时差统计”本质上是一样的,只是提取了一层,稍微优雅一些??。注意:您可以根据自己的业务需要修改TraceWatch的内部数据结构。为了简单起见,内部数据结构只是一个例子。3.进阶方法第二节提到的两种方法,说白了就是“直截了当”。我们也可以尝试更轻松地编写代码。3.1函数在jdk1.8中,引入了java.util.function包。通过该类提供的接口,可以实现在指定代码段的上下文中执行附加代码的功能。publicclassTraceHolderTest{publicstaticvoidmain(String[]args){TraceWatchtraceWatch=newTraceWatch();treetholder.run(traceWatch,“function1”,i->{尝试{timeunit.seconds.sleep(1);////模拟模拟}catch(InterruptedExceptione){e.printstacktrace();}}});字符串结果=preetholder.run(traceWatch,“function2”,()->{尝试{timeunit.seconds.sleep(1);//模拟模拟代码代码返回“是”;}catch(InterruptedExceptione){e.printstacktrace();返回“否”;}});treetholder.run(traceWatch,“function1”,i->{尝试{timeunit.seconds.sleep(1);////模拟模拟}catch(InterruptedExceptione){e.printstacktrace();}}});System.out.println(JSON.toJSONString(traceWatch.getTaskMap()));}}/*输出:{"function2":[{"data":1004,"taskName":"function2"}],"function1":[{"data":1001,"taskName":"function1"},{"data":1002,"taskName":"function1"}]}*/``````publicclassTraceHolder{/***调用返回值*/publicstaticTrun(TraceWatchtraceWatch,StringtaskName,Suppliersupplier){try{traceWatch.start(taskName);返回supplier.get();}最后{traceWatch.stop();}}/***无返回值调整*/publicstaticvoidrun(TraceWatchtraceWatch,StringtaskName,IntConsumerfunction){try{traceWatch.start(taskName);功能.接受(0);}最后{traceWatch.stop();}}}这里我利用了Supplier和IntConsumer接口对外提供有返回值和无返回值。在TraceHolder类中,核心代码块前后,分别调用前面的TraceWatch方法,实现耗时统计功能。3.2AutoCloseable除了使用Function特性外,我们还可以使用jdk1.7的AutoCloseable特性。说起AutoCloseable,可能有些同学没有听说过,但是如果给大家看下面的代码,大家马上就会明白它是什么了。//未使用AutoCloseablepublicstaticStringreadFirstLingFromFile(Stringpath)throwsIOException{BufferedReaderbr=null;试试{br=newBufferedReader(newFileReader(path));返回br.readLine();}catch(IOExceptione){e.打印堆栈跟踪();}最后{if(br!=null){br.close();}}returnnull;}//使用AutoCloseablepublicstaticStringreadFirstLineFromFile(Stringpath)throwsIOException{try(BufferedReaderbr=newBufferedReader(newFileReader(path))){returnbr.readLine();}}一个实现了AutoCloseable接口的对象可以在try之后加载,作用于整个try语句块,执行完方法后回调AutoCloseable#close()。我们来改造TraceWatch类:实现AutoCloseable接口,实现close()接口:@Overridepublicvoidclose(){this.stop();}修改start()方法支持链式调用:publicTraceWatchstart(StringtaskName)抛出IllegalStateException{if(this.currentTaskName!=null){抛出新的IllegalStateException(“无法启动TraceWatch:它已经在运行”);}this.currentTaskName=taskName;this.startMs=TimeUtils.nowMs();返回这个;}``````publicclassAutoCloseableTest{publicstaticvoidmain(String[]args){TraceWatchtraceWatch=newTraceWatch();try(TraceWatchignored=traceWatch.start("function1")){Unit{Timetry.SECONDS.sleep(1);//模拟业务代码}catch(InterruptedExceptione){e.printStackTrace();}}try(TraceWatchignored=traceWatch.start("function2")){try{TimeUnit.SECONDS.sleep(1);//模拟业务代码}catch(InterruptedExcalcione){{e.printStRInce());}fry(travelwordignored=trablewable.tart.tart("frynet1")){{tIIMENIT.SleEP(1);//模拟业务代码}catCH(inte.printStackTrace();}}}System.out.println(JSON.toJSONString(traceWatch.getTaskMap()));}}/*output:{"function2":[{"data":1001,"taskName":"function2"}],"function1":[{"data":1002,"taskName":"function1"},{"data":1002,"taskName":"function1"}]}*/四、总结本文列表统计耗时代码的四种方法:时间差统计StopWatchFunctionAutoCloseable列出的方案是我目前能想到的