平常项目开发中,会需要处理这样的需求:统计某段代码,或者某个方法的执行耗时。而且这个需求实现起来并不难,很多小伙伴都会编写如下代码
public static void main(String[] args) {
// 代码执行耗时统计
// 记时开始-start
long start = System.currentTimeMillis();
// 模拟代码实际执行时间(休眠800毫秒)
try {
TimeUnit.MILLISECONDS.sleep(800);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 记时结束-end
long end = System.currentTimeMillis();
System.out.println("代码执行完成.共耗时:" + (end - start) + "毫秒.");
}
执行结果
代码执行完成.共耗时:802毫秒.
Process finished with exit code 0
像上面这样的代码,初步看来没有什么问题,需求已经实现了!但是我们要是细想,这样的代码真的 ok 吗?
答案肯定是不 ok 的,这样的代码不够优雅!我们其实有更好的实现方式,今天这篇文章,我就给你分享一个好用的代码执行耗时统计工具,你可以借鉴用到你们的项目中
1. 工具类实现分析
首先我们尝试思考一下,要编写这样一个代码执行耗时工具类,需要考虑哪些点呢?
- 既然是一个工具类,肯定是大家都要用的,要考虑并发场景下线程安全的问题
- 该工具类提供的能力,最好是能支持嵌套统计代码执行耗时
需求提出来了,实现其实非常简单,要考虑并发场景下线程安全的问题,通常有两种解决方案
- 加锁,能保证线程安全,但是性能肯定上不去了 —> 因此这不是推荐的方案
- ThreadLocal,与当前线程绑定,不存在共享问题,即解决了线程安全的问题,又不影响性能 —> 空间换时间,是推荐的方案
2. 代码:TimerStat
实现思路清楚以后,代码其实非常简单,我就直接上代码,你一看就明白了。
public class TimerStat {
/**
* 当前线程绑定ThreadLocal
*/
private static final ThreadLocal<List<Long>> TIMER_STAT_THREAD_LOCAL = new ThreadLocal();
/**
* 耗时统计-开始
* @return
*/
public static long start(){
// 系统当前时间
long time = System.currentTimeMillis();
// 从threadLocal取值
List<Long> times = TIMER_STAT_THREAD_LOCAL.get();
// 第一次统计耗时
if(Objects.isNull(times)){
times = new ArrayList<>();
TIMER_STAT_THREAD_LOCAL.set(times);
}
// 将time添加到list,支持嵌套
times.add(time);
return time;
}
/**
* 耗时统计-结束
* @return
*/
public static long end(){
// 从threadLocal取值
List<Long> times = TIMER_STAT_THREAD_LOCAL.get();
if(Objects.nonNull(times)){
// 支持嵌套,从最后一个开始取值
Long time = times.remove(times.size() - 1);
long end = System.currentTimeMillis();
time = end - time;
return time;
}
return -1L;
}
}
使用代码示例
// 代码执行耗时统计
// 记时开始-start
TimerStat.start();
// 模拟代码实际执行时间(休眠800毫秒)
try {
TimeUnit.MILLISECONDS.sleep(800);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("TimerStat统计.代码执行完成.共耗时:" + TimerStat.end() + "毫秒.");
执行结果
TimerStat统计.代码执行完成.共耗时:801毫秒.
Process finished with exit code 0