spring提供的StopWatch类可以帮助我们收集方法耗时,但是缺点是只能在一个方法内收集,不支持多个方法内统一收集
gitee地址
github地址
优化点:
1、可以在多线程下使用
2、可以跨方法使用
KStopWatch.class
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
public class KStopWatch {
private final static ConcurrentHashMap counter = new ConcurrentHashMap();
private final List completeTaskList;
private final static KStopWatch stopWatch = new KStopWatch();
private KStopWatch(){
completeTaskList = new ArrayList(128);
}
public static KStopWatch getInstance(){
return stopWatch;
}
public void start(String taskName){
String key = taskName + ";" + Thread.currentThread().getId();
Long[] times = new Long[2];
times[0] = 0L;
times[1] = System.currentTimeMillis();
Long[] oldTime = counter.putIfAbsent(key, times);
if(oldTime != null){
if(oldTime[0] == null){
oldTime[0] = 0L;
}else{
oldTime[0] += System.currentTimeMillis() - oldTime[1];
}
oldTime[1] = System.currentTimeMillis();
counter.put(key, oldTime);
}
}
public void stop(String taskName){
String key = taskName + ";" + Thread.currentThread().getId();
Long[] times = counter.get(key);
if(times == null || times[1] == null){
throw new RuntimeException("taskName:"+key+" not start!");
}
times[0] += System.currentTimeMillis() - times[1];
completeTaskList.add(new KStopWatch.TaskInfo(key, times[0]));
counter.remove(key);
}
@Override
public synchronized String toString() {
//StringBuilder sb = new StringBuilder("completeTask:" + completeTaskList.size() + "\n");
StringBuilder sb = new StringBuilder("total:\n");
Map totalMap = new HashMap(128);
Map detailMap = new HashMap(128);
for (int i = 0; i < completeTaskList.size(); i++) {
TaskInfo taskInfo = completeTaskList.get(i);
String method = taskInfo.getTaskName().split(";")[0];
if(!totalMap.containsKey(method)){
totalMap.put(method, 0L);
}
Long times = totalMap.get(method);
times += taskInfo.getTimeSeconds();
totalMap.put(method, times);
if(!detailMap.containsKey(taskInfo.getTaskName())){
detailMap.put(taskInfo.getTaskName(), 0L);
}
Long times1 = detailMap.get(taskInfo.getTaskName());
times1 += taskInfo.getTimeSeconds();
detailMap.put(taskInfo.getTaskName(), times1);
completeTaskList.remove(taskInfo);
i--;
}
sb.append(totalMap);
sb.append("\n");
sb.append("detail:\n");
sb.append(detailMap);
return sb.toString();
}
public static final class TaskInfo {
private final String taskName;
private final long timeMillis;
TaskInfo(String taskName, long timeMillis) {
this.taskName = taskName;
this.timeMillis = timeMillis;
}
public String getTaskName() {
return this.taskName;
}
public long getTimeMillis() {
return this.timeMillis;
}
public long getTimeSeconds() {
return TimeUnit.MILLISECONDS.toSeconds(this.timeMillis);
}
public double getTimeMinutes() { return TimeUnit.MILLISECONDS.toMinutes(this.timeMillis); }
}
}
测试类:KStopWatchTest.class
import java.util.Random;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
public class KStopWatchTest {
public static void main(String[] args) throws InterruptedException {
KStopWatchTest test = new KStopWatchTest();
// 线程运行
// test.threadTest();
// 线程池运行
test.threadPoolTest();
}
public void threadTest() throws InterruptedException {
KStopWatch sw = KStopWatch.getInstance();
for (int i = 0; i < 3; i++) {
new Thread(new Runnable() {
@Override
public void run() {
long st = System.currentTimeMillis();
sw.start("计算threadTest耗时");
try {
Thread.sleep((new Random().nextInt(2)+1)*1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
long ed = System.currentTimeMillis();
System.out.println(Thread.currentThread().getId() + "-耗时:" + TimeUnit.MILLISECONDS.toSeconds(ed-st) + " s");
sw.stop("计算threadTest耗时");
}
}).start();
}
Thread.sleep(11 * 1000);
System.out.println(sw.toString());
}
public void threadPoolTest() throws InterruptedException {
ExecutorService pool =
new ThreadPoolExecutor(5, 5, 30L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue());
ExecutorService pool1 =
new ThreadPoolExecutor(5, 5, 30L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue());
ExecutorService pool2 =
new ThreadPoolExecutor(5, 5, 30L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue());
KStopWatch sw = KStopWatch.getInstance();
for (int i = 0; i < 10; i++) {
pool.submit(() -> {
long st = System.currentTimeMillis();
sw.start("代码A");
try {
Thread.sleep((new Random().nextInt(4)+1)*1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
long ed = System.currentTimeMillis();
//System.out.println(Thread.currentThread().getName() + "-耗时:" + TimeUnit.MILLISECONDS.toSeconds(ed-st) + " s");
sw.stop("代码A");
});
}
for (int i = 0; i < 10; i++) {
pool1.submit(() -> {
long st = System.currentTimeMillis();
sw.start("代码B");
try {
Thread.sleep((new Random().nextInt(4)+1)*1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
long ed = System.currentTimeMillis();
//System.out.println(Thread.currentThread().getName() + "-耗时:" + TimeUnit.MILLISECONDS.toSeconds(ed-st) + " s");
sw.stop("代码B");
});
}
for (int i = 0; i < 10; i++) {
pool2.submit(() -> {
long st = System.currentTimeMillis();
sw.start("代码C");
try {
Thread.sleep((new Random().nextInt(4)+1)*1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
long ed = System.currentTimeMillis();
//System.out.println(Thread.currentThread().getName() + "-耗时:" + TimeUnit.MILLISECONDS.toSeconds(ed-st) + " s");
sw.stop("代码C");
});
}
Thread.sleep(60 * 1000);
System.out.println(KStopWatch.getInstance().toString());
pool.shutdown();
pool1.shutdown();
pool2.shutdown();
}
}
测试结果(单位是秒):
total:
{代码B=27, 代码C=29, 代码A=24}
detail:
{代码B;17=5, 代码B;16=6, 代码B;19=7, 代码B;18=5, 代码C;24=4, 代码C;25=8, 代码C;22=6, 代码C;23=6, 代码C;21=5, 代码A;15=7, 代码A;12=4, 代码A;11=4, 代码A;14=4, 代码A;13=5, 代码B;20=4}