相信大家在实际工具中,经常需要做性能分析,做性能分析就少不了代码执行的耗时分析,以下是比较实用的耗时统计工具,我在项目中应用过,使用比较方便
import logger.Logger;
import logger.LoggerFactory;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.Timer;
import java.util.TimerTask;
/**
* 线程安全的时间监控工具类
*/
public class TimeMonitor {
// 使用 ThreadLocal 确保每个线程有独立的统计状态
private static final ThreadLocal<ThreadStats> THREAD_STATS = ThreadLocal.withInitial(ThreadStats::new);
// 清理定时器(全局一个即可)
private static volatile Timer timer = null;
private static final Object TIMER_LOCK = new Object();
private static final Logger logger = LoggerFactory.getLogger();
/**
* 线程独立的统计状态
*/
private static class ThreadStats {
// 使用 ConcurrentHashMap 存储开始时间
private final Map<String, Long> startTimeMap = new ConcurrentHashMap<>();
private final StringBuilder stringBuilder = new StringBuilder();
private final AtomicInteger taskCounter = new AtomicInteger(0);
private volatile long lastAccessTime = System.currentTimeMillis();
public void updateAccessTime() {
lastAccessTime = System.currentTimeMillis();
}
}
/**
* 开始统计某个任务的耗时
* @param taskName 任务名(可区分不同统计点)
*/
public static void start(String taskName) {
ThreadStats stats = THREAD_STATS.get();
stats.updateAccessTime();
logger.i("TimeMonitor", "Start monitoring task: " + taskName);
stats.startTimeMap.put(taskName, System.currentTimeMillis());
// 启动或重置清理定时器
scheduleCleanup();
}
/**
* 结束统计
* @param taskName 任务名
* @param isLast 是否为最后的任务
*/
public static void end(String taskName, boolean isLast) {
ThreadStats stats = THREAD_STATS.get();
stats.updateAccessTime();
logger.i("TimeMonitor", "End monitoring task: " + taskName + ", isLast: " + isLast);
Long startTime = stats.startTimeMap.remove(taskName);
if (startTime == null) {
return;
}
long duration = System.currentTimeMillis() - startTime;
int taskCount = stats.taskCounter.incrementAndGet();
// 构建日志信息
String logEntry = taskCount + "." + taskName + ", 耗时: " + duration + " ms\n";
stats.stringBuilder.append(logEntry);
if (isLast) {
String finalLog = stats.stringBuilder.toString();
logger.i("TimeMonitor", finalLog);
// 清空当前线程的状态
clearCurrentThread();
} else {
logger.i("TimeMonitor", taskCount + ". " + taskName + ", 耗时: " + duration + " ms");
}
}
/**
* 结束统计(非最后任务)
*/
public static void end(String taskName) {
end(taskName, false);
}
/**
* 清理当前线程的所有统计数据
*/
public static void clearCurrentThread() {
ThreadStats stats = THREAD_STATS.get();
String log = stats.stringBuilder.toString();
if (!log.isEmpty()) {
logger.i("TimeMonitor", "清理线程 " + Thread.currentThread().getName() + " 的统计数据:\n" + log);
}
// 创建新的实例,避免复用旧数据
THREAD_STATS.remove();
}
/**
* 清理所有线程的统计数据(谨慎使用,通常用于应用关闭时)
*/
public static void clearAll() {
synchronized (TIMER_LOCK) {
if (timer != null) {
timer.cancel();
timer = null;
}
}
// 清理所有线程的 ThreadLocal 数据
THREAD_STATS.remove();
logger.i("TimeMonitor", "已清理所有线程的统计数据");
}
/**
* 调度清理任务
*/
private static void scheduleCleanup() {
synchronized (TIMER_LOCK) {
if (timer != null) {
return; // 定时器已经在运行
}
timer = new Timer("TimeMonitor-CleanupTimer", true); // 使用守护线程
timer.schedule(new TimerTask() {
@Override
public void run() {
cleanupInactiveThreads();
}
}, 10000, 10000); // 10秒后开始,每10秒执行一次
}
}
/**
* 清理不活跃线程的数据
*/
private static void cleanupInactiveThreads() {
// 注意:ThreadLocal 通常不需要显式清理,这里只是为了示例
// 实际项目中,确保在线程结束时调用 clearCurrentThread()
// 这里可以添加自定义的清理逻辑,比如:
// 1. 监控长时间未完成的任务
// 2. 清理异常线程的残留数据
logger.i("TimeMonitor", "执行定时清理检查");
}
/**
* 获取当前线程的统计信息(用于调试)
*/
public static String getCurrentThreadStats() {
ThreadStats stats = THREAD_STATS.get();
return "任务数量: " + stats.taskCounter.get() +
", 进行中任务: " + stats.startTimeMap.size() +
", 最后访问: " + stats.lastAccessTime;
}
}
使用方法
public static void main(String[] args) {
// 在多线程环境中安全使用
new Thread(() -> {
TimeMonitor.start("任务1");
// 执行一些操作...
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
TimeMonitor.end("任务1");
TimeMonitor.start("任务2");
// 执行更多操作...
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
TimeMonitor.end("任务2", true); // 最后一个任务
}).start();
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
输出结果
TimeMonitor.clearCurrentThread(TimeMonitor.java:100): info[TimeMonitor] 清理线程 Thread-0 的统计数据:
1.任务1, 耗时: 1017 ms
2.任务2, 耗时: 2002 ms