无障碍 关怀版

这 4 种,统计代码执行耗时,才足够优雅!

原标题:这 4 种,统计代码执行耗时,才足够优雅!

上一篇: Android 13 正式登场 !!

目录

  • 01、前言

  • 02、常规方法

  • 03、时间差统计

  • 04、StopWatch

  • 05、高级方法

  • 06、Function

  • 07、AutoCloseable

  • 08、总结

01、前言

02、常规方法

03、时间差统计

04、StopWatch

05、高级方法

06、Function

07、AutoCloseable

08、总结

代码耗时统计在日常开发中算是一个十分常见的需求,特别是在需要找出代码性能瓶颈时。

可能也是受限于 Java 的语言特性,总觉得代码写起来不够优雅,大量的耗时统计代码,干扰了业务逻辑。特别是开发功能的时候,有个感受就是刚刚开发完代码很清爽优雅,结果加了一大堆辅助代码后,整个代码就变得臃肿了,自己看着都挺难受。因此总想着能不能把这块写的更优雅一点,今天本文就尝试探讨下“代码耗时统计”这一块。

在开始正文前,先说下前提,“代码耗时统计”的并不是某个方法的耗时,而是任意代码段之间的耗时。这个代码段,可能是一个方法中的几行代码,也有可能是从这个方法的某一行到另一个被调用方法的某一行,因此 「通过 AOP 方式是不能实现这个需求的」

二、常规方法2.1 时间差统计

这种方式是最简单的方法,记录下开始时间,再记录下结束时间,计算时间差即可。

publicclassTimeDiffTest{

publicstaticvoidmain(String[] args)throwsInterruptedException {

finallongstartMs = TimeUtils.nowMs;

TimeUnit.SECONDS.sleep( 5); // 模拟业务代码

System.out.println( "timeCost: "+ TimeUtils.diffMs(startMs));

}

}

publicclassTimeUtils{

/**

* @return当前毫秒数

*/

publicstaticlongnowMs{

returnSystem.currentTimeMillis;

}

/**

* 当前毫秒与起始毫秒差

* @paramstartMillis 开始纳秒数

* @return时间差

*/

publicstaticlongdiffMs( longstartMillis) {

returndiffMs(startMillis, nowMs);

}

}

这种方式的优点是实现简单,利于理解;缺点就是对代码的侵入性较大,看着很傻瓜,不优雅。

2.2 StopWatch

第二种方式是参考 StopWatch,StopWatch 通常被用作统计代码耗时,各个框架和 Common 包都有自己的实现。

publicclassTraceWatchTest{

publicstaticvoidmain(String[] args)throwsInterruptedException {

TraceWatch traceWatch = 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));

}

}

/* output:

{"function2":[{"data":1000,"taskName":"function2"}],"function1":[{"data":1000,"taskName":"function1"},{"data":1,"taskName":"function1"}]}

*/

publicclassTraceWatch{

/** Start time of the current task. */

privatelongstartMs;

/** Name of the current task. */

@Nullable

privateString currentTaskName;

@Getter

privatefinalMap<String, List<TaskInfo>> taskMap = newHashMap<>;

/**

* 开始时间差类型指标记录,如果需要终止,请调用 { @link#stop}

*

* @paramtaskName 指标名

*/

publicvoidstart(String taskName)throwsIllegalStateException {

if( this.currentTaskName != null) {

thrownewIllegalStateException( "Can't start TraceWatch: it's already running");

}

this.currentTaskName = taskName;

this.startMs = TimeUtils.nowMs;

}

/**

* 终止时间差类型指标记录,调用前请确保已经调用

*/

publicvoidstopthrowsIllegalStateException {

if( this.currentTaskName == null) {

thrownewIllegalStateException( "Can't stop TraceWatch: it's not running");

}

longlastTime = TimeUtils.nowMs - this.startMs;

TaskInfo info = newTaskInfo( this.currentTaskName, lastTime);

this.taskMap.computeIfAbsent( this.currentTaskName, e -> newLinkedList<>).add(info);

this.currentTaskName = null;

}

/**

* 直接记录指标数据,不局限于时间差类型

* @paramtaskName 指标名

* @paramdata 指标数据

*/

publicvoidrecord(String taskName, Object data){

TaskInfo info = newTaskInfo(taskName, data);

this.taskMap.computeIfAbsent(taskName, e -> newLinkedList<>).add(info);

}

@Getter

@AllArgsConstructor

publicstaticfinalclassTaskInfo{

privatefinalString taskName;

privatefinalObject data;

}

}

我是仿照 org.springframework.util.StopWatch的实现,写了 TraceWatch类,这个方法提供了两种耗时统计的方法:

通过调用 Start(name)Stop方法,进行耗时统计。

通过调用 Record(name, timeCost),方法,直接记录耗时信息。

这种方式本质上和“时间差统计”是一致的,只是抽取了一层,稍微优雅了一点。

注:你可以根据自己的业务需要,自行修改 TraceWatch 内部的数据结构,我这里简单起见,内部的数据结构只是随便举了个例子。

注:你可以根据自己的业务需要,自行修改 TraceWatch 内部的数据结构,我这里简单起见,内部的数据结构只是随便举了个例子。

第二节提到的两种方法,用大白话来说都是“直来直去”的感觉,我们还可以尝试把代码写的更简便一点。

3.1 Function

在 jdk 1.8 中,引入了 java.util.function 包,通过该类提供的接口,能够实现在指定代码段的上下文执行额外代码的功能。

publicclassTraceHolderTest{

publicstaticvoidmain(String[] args){

TraceWatch traceWatch = newTraceWatch;

TraceHolder.run(traceWatch, "function1", i -> {

try{

TimeUnit.SECONDS.sleep( 1); // 模拟业务代码

} catch(InterruptedException e) {

e.printStackTrace;

}

});

String result = TraceHolder.run(traceWatch, "function2", -> {

try{

TimeUnit.SECONDS.sleep( 1); // 模拟业务代码

return"YES";

} catch(InterruptedException e) {

e.printStackTrace;

return"NO";

}

});

TraceHolder.run(traceWatch, "function1", i -> {

try{

TimeUnit.SECONDS.sleep( 1); // 模拟业务代码

} catch(InterruptedException e) {

e.printStackTrace;

}

});

System.out.println(JSON.toJSONString(traceWatch.getTaskMap));

}

}

/* output:

{"function2":[{"data":1004,"taskName":"function2"}],"function1":[{"data":1001,"taskName":"function1"},{"data":1002,"taskName":"function1"}]}

*/

publicclassTraceHolder{

/**

* 有返回值调用

*/

publicstatic<T> T run(TraceWatch traceWatch, String taskName, Supplier<T> supplier){

try{

traceWatch.start(taskName);

returnsupplier.get;

} finally{

traceWatch.stop;

}

}

/**

* 无返回值调用

*/

publicstaticvoidrun(TraceWatch traceWatch, String taskName, IntConsumer function){

try{

traceWatch.start(taskName);

function.accept( 0);

} finally{

traceWatch.stop;

}

}

}

这里我利用了 SupplierIntConsumer接口,对外提供了有返回值和无返回值得调用,在 TraceHolder类中,在核心代码块的前后,分别调用了前文的 TraceWatch 的方法,实现了耗时统计的功能。

3.2 AutoCloseable

除了利用 Function 的特性,我们还可以使用 jdk 1.7 的 AutoCloseable特性。说 AutoCloseable 可能有同学没听过,但是给大家展示下以下代码,就会立刻明白是什么东西了。

// 未使用 AutoCloseable

publicstaticString readFirstLingFromFile(String path)throwsIOException {

BufferedReader br = null;

try{

br = newBufferedReader( newFileReader(path));

returnbr.readLine;

} catch(IOException e) {

e.printStackTrace;

} finally{

if(br != null) {

br.close;

}

}

returnnull;

}

// 使用 AutoCloseable

publicstaticString readFirstLineFromFile(String path)throwsIOException {

try(BufferedReader br = newBufferedReader( newFileReader(path))) {

returnbr.readLine;

}

}

try后方可以加载一个实现了 AutoCloseable 接口的对象,该对象作用于整个 try 语句块中,并且在执行完毕后回调 AutoCloseable#close方法。

让我们对 TraceWatch类进行改造:

实现 AutoCloseable接口,实现 close接口:

publicvoidclose{

this.stop;

}

修改 start方法,使其支持链式调用:

if( this.currentTaskName != null) {

thrownewIllegalStateException( "Can't start TraceWatch: it's already running");

}

this.currentTaskName = taskName;

this.startMs = TimeUtils.nowMs;

returnthis;

}

publicclassAutoCloseableTest{

publicstaticvoidmain(String[] args){

TraceWatch traceWatch = newTraceWatch;

try(TraceWatch ignored = traceWatch.start( "function1")) {

try{

TimeUnit.SECONDS.sleep( 1); // 模拟业务代码

} catch(InterruptedException e) {

e.printStackTrace;

}

}

try(TraceWatch ignored = traceWatch.start( "function2")) {

try{

TimeUnit.SECONDS.sleep( 1); // 模拟业务代码

} catch(InterruptedException e) {

e.printStackTrace;

}

}

try(TraceWatch ignored = traceWatch.start( "function1")) {

try{

TimeUnit.SECONDS.sleep( 1); // 模拟业务代码

} catch(InterruptedException e) {

e.printStackTrace;

}

}

System.out.println(JSON.toJSONString(traceWatch.getTaskMap));

}

}

/* output:

{"function2":[{"data":1001,"taskName":"function2"}],"function1":[{"data":1002,"taskName":"function1"},{"data":1002,"taskName":"function1"}]}

*/

四、总结

本文列举了四种统计代码耗时的方法:

  • 时间差统计

  • StopWatch

  • Function

  • AutoCloseable

时间差统计

StopWatch

Function

AutoCloseable

列举的方案是我目前能想到的方案。当然可能有更加优雅的方案,希望有相关经验的同学能在评论区一起交流下~

来源:jitwxs.cn

全文完,感谢你的耐心阅读。如果你还想看到我的文章,请一定给本文“在看”、 “点赞”,新文章推送才会第一时间出现在你的微信里。

- END -

热门推荐:

  • 字节面试:说说零拷贝,成功上岸!

  • 为什么 OAuth 里除了 Access Token 之外,还需要 Refresh Token?

  • Java 8 重构传统设计模式,是真的优雅!

  • 用 Arthas 定位 Spring Boot 接口的超时问题,让应用起飞~
  • 7行代码让B站崩溃3小时,竟因“一个诡计多端的0”

字节面试:说说零拷贝,成功上岸!

为什么 OAuth 里除了 Access Token 之外,还需要 Refresh Token?

Java 8 重构传统设计模式,是真的优雅!

PS:如果觉得我的分享不错,欢迎大家随手点赞、转发、在看。 返回搜狐,查看更多

责任编辑:

平台声明:该文观点仅代表作者本人,搜狐号系信息发布平台,搜狐仅提供信息存储空间服务。
阅读 ()
推荐阅读

代做工资流水公司厦门代开工资证明黄冈查转账流水福州房贷银行流水 公司兰州自存银行流水代办包头代做个人银行流水烟台在职证明潮州工作收入证明代开杭州背调流水打印遵义房贷收入证明样本滁州办房贷银行流水邯郸入职银行流水代做西安制作贷款流水济南工资流水app截图模板柳州银行流水单图片无锡公司流水代开西宁贷款流水打印保定转账流水多少钱德阳查公司流水开封办理离职证明温州银行对公流水代办衡阳查公司流水宁德背调流水多少钱湘潭签证银行流水 代做金华制作签证流水桂林办理转账流水新乡代开工资流水许昌开房贷收入证明潍坊背调流水代开九江流水查询南宁贷款工资流水 公司香港通过《维护国家安全条例》两大学生合买彩票中奖一人不认账让美丽中国“从细节出发”19岁小伙救下5人后溺亡 多方发声卫健委通报少年有偿捐血浆16次猝死汪小菲曝离婚始末何赛飞追着代拍打雅江山火三名扑火人员牺牲系谣言男子被猫抓伤后确诊“猫抓病”周杰伦一审败诉网易中国拥有亿元资产的家庭达13.3万户315晚会后胖东来又人满为患了高校汽车撞人致3死16伤 司机系学生张家界的山上“长”满了韩国人?张立群任西安交通大学校长手机成瘾是影响睡眠质量重要因素网友洛杉矶偶遇贾玲“重生之我在北大当嫡校长”单亲妈妈陷入热恋 14岁儿子报警倪萍分享减重40斤方法杨倩无缘巴黎奥运考生莫言也上北大硕士复试名单了许家印被限制高消费奥巴马现身唐宁街 黑色着装引猜测专访95后高颜值猪保姆男孩8年未见母亲被告知被遗忘七年后宇文玥被薅头发捞上岸郑州一火锅店爆改成麻辣烫店西双版纳热带植物园回应蜉蝣大爆发沉迷短剧的人就像掉进了杀猪盘当地回应沈阳致3死车祸车主疑毒驾开除党籍5年后 原水城县长再被查凯特王妃现身!外出购物视频曝光初中生遭15人围殴自卫刺伤3人判无罪事业单位女子向同事水杯投不明物质男子被流浪猫绊倒 投喂者赔24万外国人感慨凌晨的中国很安全路边卖淀粉肠阿姨主动出示声明书胖东来员工每周单休无小长假王树国卸任西安交大校长 师生送别小米汽车超级工厂正式揭幕黑马情侣提车了妈妈回应孩子在校撞护栏坠楼校方回应护栏损坏小学生课间坠楼房客欠租失踪 房东直发愁专家建议不必谈骨泥色变老人退休金被冒领16年 金额超20万西藏招商引资投资者子女可当地高考特朗普无法缴纳4.54亿美元罚金浙江一高校内汽车冲撞行人 多人受伤

代做工资流水公司 XML地图 TXT地图 虚拟主机 SEO 网站制作 网站优化