如何使用ExecutorService跟踪任务执行统计信息?

时间:2009-05-28 20:38:54

标签: java concurrency statistics monitoring

我正在使用ExecutorService解雇任务,调度需要按任务特定条件分组的任务:

Task[type=a]
Task[type=b]
Task[type=a]
...

我希望定期输出每项任务所用的平均时间长度(按type分组)以及平均值/中位数和标准差等统计信息。

当然,这需要非常快,理想情况下不应该在报告统计信息时导致各个线程同步。这样做有什么好的架构?

5 个答案:

答案 0 :(得分:9)

ThreadPoolExecutor提供了您可以覆盖的beforeExecuteafterExecute方法。您可以使用它们将统计信息记录在单个(ExecutorService的成员变量)ConcurrentHashMap中,以便为您的任务键入一些唯一标识符,并存储类型,开始时间和结束时间。

当您准备好查看ConcurrentHashMap时,计算{{1}}的统计数据。

答案 1 :(得分:4)

子类Thread Pool Executor并跟踪执行事件:

值得注意的是,这些方法是由执行任务的工作线程调用的,因此您需要确保执行跟踪代码的线程安全。

此外,您将收到的Runnables很可能不是您的Runnables,而是包含在FutureTasks中。

答案 2 :(得分:2)

另一种方法是使用包装器/装饰器模式。

public class Job implements Runnable {
private Runnable _task;
private Statistics _statistics;

public Job(Runnable task, Statistics statistics) {
    this._task = task;
}

public void run() {
    long s = System.currentTimeMillis();
    _task.run();
    long e = System.currentTimeMillis();

    long executionTime = e - s;
    _statistics.updateStatistics(executionTime);
}
}

答案 3 :(得分:1)

我相信其他两个答案是正确的,但可能有点过于复杂(虽然我的回答虽然简单,但可能不如他们的那么高。

为什么不使用原子变量来跟踪您的统计数据?比如运行的任务数,总执行时间(除以总数,你获得平均执行时间)。将这些变量传递给每个任务的Runnable。除非您的任务非常短暂,否则我认为锁定原子变量的开销不会对您产生影响。

答案 4 :(得分:0)

我同意@Robert Munteanu。尽管文档说它可以用于统计,但线程池中的beforeExecute确实一文不值。但是实际上,我们无法根据自己的情况检查可运行对象的身份。

我认为包装纸可以到达这个。

public interface ICallableHook<V> {
    void beforeExecute(Thread t, Callable<V> callable);
    void afterExecute(Callable<V> callable, V result, Throwable e);
}


private class CallableWrapper<V> implements Callable<V> {
        private ICallableHook hooker;
        private Callable<V> callable;

        CallableWrapper(Callable callable, ICallableHook hooker) {
            this.callable = callable;
            this.hooker = hooker;
        }




    @Override
    public V call() throws Exception {
        if (hooker != null) {
            hooker.beforeExecute(Thread.currentThread(), callable);
        }

        V result = null;
        Exception exception = null;
        try {
            result = callable.call();
        } catch (Exception e) {
            exception = e;
            throw e;
        } finally {
            if (hooker != null) {
                hooker.afterExecute(callable, result, exception);
            }
        }
        return result;
    }
}

这样的用法

  for (Callable<XXX> callable : callableList) {
        CallableWrapper<XXX> callableWrapper = new CallableWrapper<>(callable, hooker);
        Future task = completionService.submit(callableWrapper);

    }