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

黑马Java第二阶段:JavaWeb(笔记) 阅读详情

一、前言

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

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

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

二、常规方法

2.1 时间差统计

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

public class TimeDiffTest {
    public static void main(String[] args) throws InterruptedException {
        final long startMs = TimeUtils.nowMs();

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

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

/* output: 
timeCost: 5005
*/
public class TimeUtils {
    /**
     * @return 当前毫秒数
     */
    public static long nowMs() {
        return System.currentTimeMillis();
    }

    /**
     * 当前毫秒与起始毫秒差
     * @param startMillis 开始纳秒数
     * @return 时间差
     */
    public static long diffMs(long startMillis) {
       return diffMs(startMillis, nowMs());
    }
}

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

2.2 StopWatch

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

public class TraceWatchTest {
    public static void main(String[] args) throws InterruptedException {
        TraceWatch traceWatch = new TraceWatch();

        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"}]}
*/

public class TraceWatch {
    /** Start time of the current task. */
    private long startMs;

    /** Name of the current task. */
    @Nullable
    private String currentTaskName;

    @Getter
    private final Map<String, List<TaskInfo>> taskMap = new HashMap<>();

    /**
     * 开始时间差类型指标记录,如果需要终止,请调用 {@link #stop()}
     *
     * @param taskName 指标名
     */
    public void start(String taskName) throws IllegalStateException {
        if (this.currentTaskName != null) {
            throw new IllegalStateException("Can't start TraceWatch: it's already running");
        }
        this.currentTaskName = taskName;
        this.startMs = TimeUtils.nowMs();
    }

    /**
     * 终止时间差类型指标记录,调用前请确保已经调用
     */
    public void stop() throws IllegalStateException {
        if (this.currentTaskName == null) {
            throw new IllegalStateException("Can't stop TraceWatch: it's not running");
        }
        long lastTime = TimeUtils.nowMs() - this.startMs;

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

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

        this.currentTaskName = null;
    }

    /**
     * 直接记录指标数据,不局限于时间差类型
     *  @param taskName 指标名
     * @param data 指标数据
     */
    public void record(String taskName, Object data) {
        TaskInfo info = new TaskInfo(taskName, data);

        this.taskMap.computeIfAbsent(taskName, e -> new LinkedList<>()).add(info);
    }

    @Getter
    @AllArgsConstructor
    public static final class TaskInfo {
        private final String taskName;

        private final Object data;
    }
}

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

通过调用 Start(name) 和 Stop() 方法,进行耗时统计。通过调用 Record(name, timeCost),方法,直接记录耗时信息。这种方式本质上和“时间差统计”是一致的,只是抽取了一层,稍微优雅了一点。

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

三、高级方法

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

3.1 Function

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

public class TraceHolderTest {
    public static void main(String[] args) {
        TraceWatch traceWatch = new TraceWatch();

        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"}]}
*/

public class TraceHolder {
    /**
     * 有返回值调用
     */
    public static <T> T run(TraceWatch traceWatch, String taskName, Supplier<T> supplier) {
        try {
            traceWatch.start(taskName);

            return supplier.get();
        } finally {
            traceWatch.stop();
        }
    }

    /**
     * 无返回值调用
     */
    public static void run(TraceWatch traceWatch, String taskName, IntConsumer function) {
        try {
            traceWatch.start(taskName);

            function.accept(0);
        } finally {
            traceWatch.stop();
        }
    }
}

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

3.2 AutoCloseable

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

// 未使用 AutoCloseable
public static String readFirstLingFromFile(String path) throws IOException {
    BufferedReader br = null;
    try {
        br = new BufferedReader(new FileReader(path));
        return br.readLine();
    } catch (IOException e) {
        e.printStackTrace();
    } finally {
        if (br != null) {
            br.close();
        }
    }
    return null;
}

// 使用 AutoCloseable
public static String readFirstLineFromFile(String path) throws IOException {
    try (BufferedReader br = new BufferedReader(new FileReader(path))) {
        return br.readLine();
    }
}

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

让我们对 TraceWatch 类进行改造:

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

@Override
public void close() {
    this.stop();
}

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

public TraceWatch start(String taskName) throws IllegalStateException {
    if (this.currentTaskName != null) {
        throw new IllegalStateException("Can't start TraceWatch: it's already running");
    }
    this.currentTaskName = taskName;
    this.startMs = TimeUtils.nowMs();
    
    return this;
}

public class AutoCloseableTest {
    public static void main(String[] args) {
        TraceWatch traceWatch = new TraceWatch();

        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

列举的方案是我目前能想到的方案。

来源 | https://jitwxs.cn/5aa91d10.html

【光子AI 2026 企业级 Agent 架构指南】别再把 Skill 当 Tool:Agent Skills × MCP 企业级落地全指南(最新定义澄清 + 场景大全 + 选型决策树+安全工程清单) 摘要: 2026年企业级AI架构迎来两大核心标准——Agent Skills与MCP(Model Context Protocol),分别解决流程标准化与系统集成难题。Agent Skills通过“文件夹化能力包”(SOP+脚本+模板)固化专家流程,确保输出一致性;MCP则以类USB-C的协议统一外部系统接入,降低集成成本。两者差异显著:Skills专注程序性知识(如财报生成),MCP侧重动态连接(如跨平台数据调用)。实际场景中,复杂需求(如智能客服)需组合使用——MCP为骨架提供连接能力,Skills为大 阅读详情

相关推荐

SPEI计算避坑指南:gma中Distribution和FitMethod参数怎么选?

本文深入探讨了在SPEI计算中如何科学选择gma库中的Distribution和FitMethod参数,帮助研究者避开常见陷阱。详细分析了LogLogistic、Gamma和Pearson3三种分布的适用场景,以及LMoment与MLE拟合方法的优缺点,为气象干旱研究提供实用指南。

weixin_30308101的博客 254

c++对的调用没有匹配的函数_利用systemtap 分析c/c++函数调用耗时

通常情况下我们开发服务时为了更好的性能往往需要借助一些工具来辅助优化,发现性能的瓶颈所在。比如:perf 用于分析哪些方法调用cpu比较高、cpu cache 命中率、分支预测等valgrind 的helgrind 工具用于分析资源竞争gprofile 用于分析一些包括函数调用次数、函数调用耗时等(编译时需要带上-pg),原理就是在函数入口、退出地方插入两个function来进行统计对源码是侵入式...

weixin_39857153的博客 430

GBase8a-GDCA认证考试-复习参考题

为提升产品服务水平定期举办GBase产品认证培训,培训更多GBase数据库专业人员,满足用户快速成长的业务需求,建设GBase产品生态。请注意甄别,根据所学知识综合判断,正确率97%(97分)。

weixin_41826637的博客 2377

分享一个统计代码耗时的小工具

分享一个统计代码耗时的小工具

weixin_55700820的博客 925

vivado 编译ADI AD9361 TCL脚本生成hdl工程

(4)输入"source ../../scripts/adi_make.tcl"命令(仅双引号内部命令,不包含双引号),个人理解:该命令用来加载当前所需要编译工程所需要各功能模块的信息,因为每个工程需要不同的功能模块,比如有的需要gpio模块,有的不需要gpio模块,这些模块上前面路径的library也同样以TCL脚本形式存在,当有需要时才进行编译,怎么知道当前工程需要什么模块呢就是通过"source ../../scripts/adi_make.tcl"(仅双引号内部命令,不包含双引号)命令实现。

weixin_40586423的博客 809

LBM模式学习·保姆级安装及初步使用教程

在学习LBM的过程中,我感觉可供参考的材料较为有限,对于一个独立学习LBM模式的初学者非常不友好(我就是这个初学者哈哈),因此萌生了写一篇保姆级的LBM模式教程,并将我踩过的坑一一指出,有助于后来者更快地上手该模式,而非半途而废。

weixin_42762673的博客 1万+

一个用于统计函数调用消耗时间的装饰器

2019独角兽企业重金招聘Python工程师标准>>> ...

weixin_34365635的博客 156

激光光束质量评估与光斑尺寸计算:从理论到MATLAB实践

本文详细解析激光光束质量评估与光斑尺寸计算的理论基础及MATLAB实践方法,重点介绍M²因子的测量与应用。通过高斯光束模型、CCD成像法和MATLAB算法实现,帮助读者掌握从理论到实践的完整流程,提升激光系统调试与评估能力。

weixin_29083373的博客 452

Linux 下 进程运行时内部函数耗时的统计 工具:pstack,strace,perf trace,systemtap

简单记录一些 在linux下 统计进程内部函数运行耗时的统计工具,主要是用作性能瓶颈分析。当然以下工具除了pstack功能单一之外,其他的工具都非常强大,这里仅仅整理特定场景的特定用法,用作协同分析。 以下工具需要追踪具体的进程,如果想要打印信息更全,建议编译的时候将符号信息都编译到二进制文件之中,-g选项 strace strace -tttT -f -p $pid -o $save_file_name 追踪指定进程内部所有线程调用到的系统调用运行耗时,单位是秒,将统计结果保存到save_file_

天行健,地势坤 7323

linux内核协议栈 TCP选项之SACK选项概述

目录 1SACK概述 1.1 SACK允许选项 1.2 SACK选项 2SACK允许选项的发送 2.1 SYN段发送 2.2 接收SYN段 2.2.1 struct tcp_options_received 2.2.2 tcp_parse_options() 2.3 发送SYN+ACK段 3D-SACK 1SACK概述 标准的TCP确认机制中,如果发送方发送了0-1000序号之间的数据,接收方收到了0-100、300-1000,那么接收方只能向发送方确认101,这时发送方会重...

2732

TIA protal与SCL从入门到精通(4)——软件单元使用

平台: TIA portal 15.1 1.添加软件单元 添加软件单元如下图: 关于软件单元使用基本限制,看如下文章即可 TIA protal与SCL从入门到精通(3)——软件单元基本介绍 每个软件单元是一个独立结构,所以每个软件单元的运行方式与实际PLC类似,所以首先添加主函数块: 注意:无论系统是否是具有并发性(这里不要混淆并行与并发的概念),在PLC所有主函数执行总是按主函数依次完成...

苦走一口田 7379

函数调用关系及消耗时间统计——程序概要分析工具gprof

函数调用关系及消耗时间统计——程序概要分析工具gprof 微信公众号:幼儿园的学霸 个人的学习笔记,关于OpenCV,关于机器学习, …。问题或建议,请公众号留言; 目录 文章目录函数调用关系及消耗时间统计——程序概要分析工具gprof目录简介使用方法cmake使用gprof数据分析文件内容信息说明用户时间与内核时间注意事项参考 简介 GNU gprof能够打印出程序运行中各个函数消耗的时间,...

leonardohaig的博客 4874

【升级U8+】不能将值 NULL 插入列 ‘VT_ID‘,表 ‘VoucherItems‘;列不允许有 Null 值。

不能将值 NULL 插入列 ‘VT_ID’,表 ‘UFDATA_006_2012.dbo.VoucherItems’;列不允许有 Null 值。INSERT 失败。

努力不懈的为广大客户提供优质的产品与真诚、贴心、专业的服务! 517

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

跟大家分享一下,如何在代码中,统计接口耗时,最优雅,性能最高,接下来我将介绍4种统计方式,如果你有更好的方式,欢迎文末留言区,交流 一、前言 代码耗时统计在日常开发中算是一个十分常见的需求,特别是在需要找出可能也是受限于 Java 的语言特性,总觉得代码写起来不够优雅,大量的耗时统计代码,干扰了业务逻辑。特别是开发功能的时候,有个感受就是刚刚开发完代码很清爽优雅,结果加了一大堆辅助代码后,整个代码就变得臃肿了,自己看着都挺难受。关注公众号:搜云库技术团队,回复:“面试题”,获取,高清PDF:3625页互联网

猫头虎技术团队:授渔优于赠鱼,兴趣引领智慧,探索之乐尤显珍贵。商务合作+:Libin9iOak ,万粉变现+:优快云WF,猫头虎承诺每年免费为100名C站创作者做账号流量诊断服务!全网搜:猫头虎技术团队,点击文章底部名片或直接私信我一切皆可谈,快找虎哥! 896

Devexpress16.2.6破解工具

Devexpress16.2.6破解工具Devexpress16.2.6破解工具Devexpress16.2.6破解工具Devexpress16.2.6破解工具

假人有限元模型

假人模型,车辆碰撞假人有限元模型,适合做乘员安全的研究者利用

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

点击上方“Java基基”,选择“设为星标”做积极的人,而不是积极废人!源码精品专栏原创 | Java 2020超神之路,很肝~中文详细注释的开源项目RPC 框架 Dubbo 源码解析网...

weixin_44421461的博客 205

大中型超市收银系统(可直接运行)

光步前台收银系统系统功能:商品销售、商品退货、挂单、收银员权限、会员销售、单品取消、整单取消、商品查询、脱机销售、脱机数据上传,基本资料下载,开钱箱、小票打印、收款结算,支持多币种付款方式(现金、储值卡、礼券、微信等),支持多种促销。运行环境:.net 4.0登陆信息用户名:1001 密码:123456内置商品信息,商品编码可输入00001000 - 00010000,测试效果相关功能键F5 结算 F9挂单 F11商品查询 F8退货 Insert单品取消 Del整单取消 F12开钱箱F2 会员 Ctrl+Z退出系统 Home 锁屏 相关源码待公开

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

上一篇:Android 13 正式登场 !!目录01、前言02、常规方法03、时间差统计04、StopWatch05、高级方法06、Function07、AutoCloseable08、总结一、前言代码耗时统计在日常开发中算是一个十分常见的需求,特别是在需要找出代码性能瓶颈时。可能也是受限于 Java 的语言特性,总觉得代码写起来不够优雅,大量的耗时统计代码,干扰了业务逻辑。特别是开发功能的时候,...

Java和Android架构 174

精雕软件(JDpaint) 5.5 绿色版

精雕软件(JDpaint) 5.5 绿色版精雕软件(JDpaint) 5.5 绿色版精雕软件(JDpaint) 5.5 绿色版

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

今天,跟大家分享一下,如何在代码中,统计接口耗时,最优雅,性能最高,接下来我将介绍4种统计方式,如果你有更好的方式,欢迎文末留言区,交流一、前言代码耗时统计在日常开发中算是一个十分常见的需...

hnjsjsac的博客 374

优雅统计代码耗时的4种方法!

来源:jitwxs.cn/5aa91d10.html一、前言代码耗时统计在日常开发中算是一个十分常见的需求,特别是在需要找出代码性能瓶颈时。可能也是受限于 Java 的语言特性,总觉得代码...

磊哥聊Java 897
上一篇: MyBatis动态SQL,写SQL更爽
下一篇: Spring 中经典的 9 种设计模式,打死也要记住啊!
Java思维导图
博客等级 码龄10年 306粉丝 71原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值