java 或 安卓项目中耗时统计工具类

271 阅读3分钟

相信大家在实际工具中,经常需要做性能分析,做性能分析就少不了代码执行的耗时分析,以下是比较实用的耗时统计工具,我在项目中应用过,使用比较方便

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