我正在使用ExecutorService解雇任务,调度需要按任务特定条件分组的任务:
Task[type=a]
Task[type=b]
Task[type=a]
...
我希望定期输出每项任务所用的平均时间长度(按type
分组)以及平均值/中位数和标准差等统计信息。
当然,这需要非常快,理想情况下不应该在报告统计信息时导致各个线程同步。这样做有什么好的架构?
答案 0 :(得分:9)
ThreadPoolExecutor提供了您可以覆盖的beforeExecute和afterExecute方法。您可以使用它们将统计信息记录在单个(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);
}