网站首页 > 技术文章 正文
推荐阅读:
Sping源码+Redis+Nginx+MySQL等七篇实战技术文档,阿里大佬推荐
阿里内部:2020年全技术栈文档+PPT分享,(万粉总结,回馈粉丝)
在我们的实际开发中,多多少少会遇到统计一段代码片段的耗时的情况,我们一般的写法如下
long start = System.currentTimeMillis();
try {
// .... 具体的代码段
} finally {
System.out.println("cost: " + (System.currentTimeMillis() - start));
}
上面的写法没有什么毛病,但是看起来就不太美观了,那么有没有什么更优雅的写法呢?
1. 代理方式
了解 Spring AOP 的同学可能立马会想到一个解决方法,如果想要统计某个方法耗时,使用切面可以无侵入的实现,如
// 定义切点,拦截所有满足条件的方法
@Pointcut("execution(public * com.git.hui.boot.aop.demo.*.*(*))")
public void point() {
}
@Around("point()")
public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable {
long start = System.currentTimeMillis();
try{
return joinPoint.proceed();
} finally {
System.out.println("cost: " + (System.currentTimeMillis() - start));
}
}
Spring AOP 的底层支持原理为代理模式,为目标对象提供增强功能;在 Spring 的生态体系下,使用 aop 的方式来统计方法耗时,可以说少侵入且实现简单,但是有以下几个问题
- 统计粒度为方法级别
- 类内部方法调用无法生效(详情可以参考博文:【SpringBoot 基础系列教程】AOP 之高级使用技能)
2. AutoCloseable
在 JDK1.7 引入了一个新的接口AutoCloseable, 通常它的实现类配合try{}使用,可在 IO 流的使用上,经常可以看到下面这种写法
// 读取文件内容并输出
try (Reader stream = new BufferedReader(new InputStreamReader(new FileInputStream("/tmp")))) {
List<String> list = ((BufferedReader) stream).lines().collect(Collectors.toList());
System.out.println(list);
} catch (IOException e) {
e.printStackTrace();
}
注意上面的写法中,最值得关注一点是,不需要再主动的写stream.close了,主要原因就是在try(){}执行完毕之后,会调用方法AutoCloseable#close方法;
基于此,我们就会有一个大单的想法,下一个Cost类实现AutoCloseable接口,创建时记录一个时间,close 方法中记录一个时间,并输出时间差值;将需要统计耗时的逻辑放入try(){}代码块
下面是一个具体的实现:
public static class Cost implements AutoCloseable {
private long start;
public Cost() {
this.start = System.currentTimeMillis();
}
@Override
public void close() {
System.out.println("cost: " + (System.currentTimeMillis() - start));
}
}
public static void testPrint() {
for (int i = 0; i < 5; i++) {
System.out.println("now " + i);
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) {
try (Cost c = new Cost()) {
testPrint();
}
System.out.println("------over-------");
}
执行后输出如下:
now 0
now 1
now 2
now 3
now 4
cost: 55
如果代码块抛异常,也会正常输出耗时么?
public static void testPrint() {
for (int i = 0; i < 5; i++) {
System.out.println("now " + i);
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
if (i == 3) {
throw new RuntimeException("some exception!");
}
}
}
再次输出如下,并没有问题
now 0
now 1
now 2
now 3
cost: 46
Exception in thread "main" java.lang.RuntimeException: some exception!
at com.git.hui.boot.order.Application.testPrint(Application.java:43)
at com.git.hui.boot.order.Application.main(Application.java:50)
3. 小结
除了上面介绍的两种方式,还有一种在业务开发中不太常见,但是在中间件、偏基础服务的功能组件中可以看到,利用 Java Agent 探针技术来实现,比如阿里的 arthas 就是在 JavaAgent 的基础上做了各种上天的功能,后续介绍 java 探针技术时会专门介绍
下面小结一下三种统计耗时的方式
基本写法
long start = System.currentTimeMillis();
try {
// .... 具体的代码段
} finally {
System.out.println("cost: " + (System.currentTimeMillis() - start));
}
优点是简单,适用范围广泛;缺点是侵入性强,大量的重复代码
Spring AOP
在 Spring 生态下,可以借助 AOP 来拦截目标方法,统计耗时
@Around("...")
public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable {
long start = System.currentTimeMillis();
try{
return joinPoint.proceed();
} finally {
System.out.println("cost: " + (System.currentTimeMillis() - start));
}
}
优点:无侵入,适合统一管理(比如测试环境输出统计耗时,生产环境不输出);缺点是适用范围小,且粒度为方法级别,并受限于 AOP 的使用范围
AutoCloseable
这种方式可以看做是第一种写法的进阶版
// 定义类
public static class Cost implements AutoCloseable {
private long start;
public Cost() {
this.start = System.currentTimeMillis();
}
@Override
public void close() {
System.out.println("cost: " + (System.currentTimeMillis() - start));
}
}
// 使用姿势
try (Cost c = new Cost()) {
...
}
优点是:简单,适用范围广泛,且适合统一管理;缺点是依然有代码侵入
说明
上面第二种方法看着属于最优雅的方式,但是限制性强;如果有更灵活的需求,建议考虑第三种写法,在代码的简洁性和统一管理上都要优雅很多,相比较第一种可以减少大量冗余代码
作者:一灰灰
链接:https://juejin.im/post/5e5e4cc6518825493d6a96c4
猜你喜欢
- 2024-09-21 Java并发编程:LongAdder | LongAccumulator 对比测试
- 2024-09-21 「Java技巧」优雅的统计程序的执行时间,别再用System.cur
- 2024-09-21 Flink SQL 知其所以然(九)| SQL 的时间语义
- 2024-09-21 ArrayList插入1000w条数据之后,我怀疑了jvm...
- 2024-09-21 《Java实战之内存模型》详解篇(java内存模型happens before)
- 2024-09-21 比反射更快!使用ASM获取class信息(ClassReader)
- 2024-09-21 了解Java线程优先级,更要知道对应操作系统的优先级,不然会踩坑
- 2024-09-21 让大学生写的一个计算时间的方法,有人看得出来是在做什么吗?这
- 2024-09-21 Java基础——Java多线程(Lock接口详解)
- 2024-09-21 JVM性能调优监控工具jps、jstack、jmap、jhat、jstat使用详解
- 1514℃桌面软件开发新体验!用 Blazor Hybrid 打造简洁高效的视频处理工具
- 569℃Dify工具使用全场景:dify-sandbox沙盒的原理(源码篇·第2期)
- 510℃MySQL service启动脚本浅析(r12笔记第59天)
- 486℃服务器异常重启,导致mysql启动失败,问题解决过程记录
- 485℃启用MySQL查询缓存(mysql8.0查询缓存)
- 467℃「赵强老师」MySQL的闪回(赵强iso是哪个大学毕业的)
- 446℃mysql服务怎么启动和关闭?(mysql服务怎么启动和关闭)
- 444℃MySQL server PID file could not be found!失败
- 最近发表
- 标签列表
-
- c++中::是什么意思 (83)
- 标签用于 (65)
- 主键只能有一个吗 (66)
- c#console.writeline不显示 (75)
- pythoncase语句 (81)
- es6includes (73)
- windowsscripthost (67)
- apt-getinstall-y (86)
- node_modules怎么生成 (76)
- c++int转char (75)
- static函数和普通函数 (76)
- el-date-picker开始日期早于结束日期 (70)
- js判断是否是json字符串 (67)
- checkout-b (67)
- c语言min函数头文件 (68)
- asynccallback (71)
- localstorage.removeitem (74)
- vector线程安全吗 (70)
- & (66)
- java (73)
- js数组插入 (83)
- mac安装java (72)
- eacces (67)
- 查看mysql是否启动 (70)
- 无效的列索引 (74)