Click on the blue font above and select "Star Official Account"
High-quality articles, delivered immediately
Follow the official account backstage to reply to pay or mall to obtain actual project information + video
Author: Jitwxs
Source| https://jitwxs.cn/5aa91d10.html
I. Introduction
Code time-consuming statistics is a very common requirement in daily development, especially when you need to find code performance bottlenecks.
It may also be limited by the language features of Java, and I always feel that the code is not elegant enough to write, and a large amount of time-consuming statistical code interferes with business logic. Especially when developing functions, I have a feeling that the code is refreshing and elegant just after the development. As a result, after adding a lot of auxiliary code, the whole code becomes bloated, and it is very uncomfortable to look at it myself. Therefore, I always wonder if I can write this piece more elegantly. Today, this article will try to discuss the “code time-consuming statistics” piece.
Before starting the main text, let me talk about the premise that "code time-consuming statistics" is not the time-consuming of a certain method, but the time-consuming between arbitrary code segments. This code segment may be a few lines of code in a method, or it may be from a line of this method to a line of another called method, so this requirement cannot be achieved through AOP.
Second, the conventional method
2.1 Time difference statistics
This method is the simplest method. Record the start time, record the end time, and calculate the time difference.
public class TimeDiffTest {
public static void main(String[] args) throws InterruptedException {
final long startMs = TimeUtils.nowMs();
TimeUnit.SECONDS.sleep(5); // 模拟业务代码
System.out.println("timeCost: " + TimeUtils.diffMs(startMs));
}
}
/* output:
timeCost: 5005
*/
public class TimeUtils {
/**
* @return 当前毫秒数
*/
public static long nowMs() {
return System.currentTimeMillis();
}
/**
* 当前毫秒与起始毫秒差
* @param startMillis 开始纳秒数
* @return 时间差
*/
public static long diffMs(long startMillis) {
return diffMs(startMillis, nowMs());
}
}
The advantage of this method is that it is simple to implement and easy to understand; the disadvantage is that it is more intrusive to the code, looks foolish, and is not elegant.
2.2 StopWatch
The second way is to refer to StopWatch. StopWatch is usually used as a time-consuming statistical code. Each framework and Common package has its own implementation.
public class TraceWatchTest {
public static void main(String[] args) throws InterruptedException {
TraceWatch traceWatch = new TraceWatch();
traceWatch.start("function1");
TimeUnit.SECONDS.sleep(1); // 模拟业务代码
traceWatch.stop();
traceWatch.start("function2");
TimeUnit.SECONDS.sleep(1); // 模拟业务代码
traceWatch.stop();
traceWatch.record("function1", 1); // 直接记录耗时
System.out.println(JSON.toJSONString(traceWatch.getTaskMap()));
}
}
/* output:
{"function2":[{"data":1000,"taskName":"function2"}],"function1":[{"data":1000,"taskName":"function1"},{"data":1,"taskName":"function1"}]}
*/
public class TraceWatch {
/** Start time of the current task. */
private long startMs;
/** Name of the current task. */
@Nullable
private String currentTaskName;
@Getter
private final Map<String, List<TaskInfo>> taskMap = new HashMap<>();
/**
* 开始时间差类型指标记录,如果需要终止,请调用 {@link #stop()}
*
* @param taskName 指标名
*/
public void start(String taskName) throws IllegalStateException {
if (this.currentTaskName != null) {
throw new IllegalStateException("Can't start TraceWatch: it's already running");
}
this.currentTaskName = taskName;
this.startMs = TimeUtils.nowMs();
}
/**
* 终止时间差类型指标记录,调用前请确保已经调用
*/
public void stop() throws IllegalStateException {
if (this.currentTaskName == null) {
throw new IllegalStateException("Can't stop TraceWatch: it's not running");
}
long lastTime = TimeUtils.nowMs() - this.startMs;
TaskInfo info = new TaskInfo(this.currentTaskName, lastTime);
this.taskMap.computeIfAbsent(this.currentTaskName, e -> new LinkedList<>()).add(info);
this.currentTaskName = null;
}
/**
* 直接记录指标数据,不局限于时间差类型
* @param taskName 指标名
* @param data 指标数据
*/
public void record(String taskName, Object data) {
TaskInfo info = new TaskInfo(taskName, data);
this.taskMap.computeIfAbsent(taskName, e -> new LinkedList<>()).add(info);
}
@Getter
@AllArgsConstructor
public static final class TaskInfo {
private final String taskName;
private final Object data;
}
}
I imitated org.springframework.util.StopWatch
the implementation and wrote the TraceWatch class. This method provides two time-consuming statistical methods:
Perform time-consuming statistics by calling Start(name)
and Stop()
methods. By calling Record(name, timeCost)
, method, directly record time-consuming information. This method is essentially the same as "time difference statistics", except that one layer is extracted, which is a little more elegant.
Note: You can modify the TraceWatch
internal data structure by yourself according to your business needs. I am here to keep it simple. The internal data structure is just an example.
Three, advanced methods
The two methods mentioned in the second section are "straight-forward" in the vernacular. We can also try to write the code a little easier.
3.1 Function
In jdk 1.8, a java.util.function
package was introduced . Through the interface provided by this class, the function of executing additional code in the context of a specified code segment can be realized.
public class TraceHolderTest {
public static void main(String[] args) {
TraceWatch traceWatch = new TraceWatch();
TraceHolder.run(traceWatch, "function1", i -> {
try {
TimeUnit.SECONDS.sleep(1); // 模拟业务代码
} catch (InterruptedException e) {
e.printStackTrace();
}
});
String result = TraceHolder.run(traceWatch, "function2", () -> {
try {
TimeUnit.SECONDS.sleep(1); // 模拟业务代码
return "YES";
} catch (InterruptedException e) {
e.printStackTrace();
return "NO";
}
});
TraceHolder.run(traceWatch, "function1", i -> {
try {
TimeUnit.SECONDS.sleep(1); // 模拟业务代码
} catch (InterruptedException e) {
e.printStackTrace();
}
});
System.out.println(JSON.toJSONString(traceWatch.getTaskMap()));
}
}
/* output:
{"function2":[{"data":1004,"taskName":"function2"}],"function1":[{"data":1001,"taskName":"function1"},{"data":1002,"taskName":"function1"}]}
*/
public class TraceHolder {
/**
* 有返回值调用
*/
public static <T> T run(TraceWatch traceWatch, String taskName, Supplier<T> supplier) {
try {
traceWatch.start(taskName);
return supplier.get();
} finally {
traceWatch.stop();
}
}
/**
* 无返回值调用
*/
public static void run(TraceWatch traceWatch, String taskName, IntConsumer function) {
try {
traceWatch.start(taskName);
function.accept(0);
} finally {
traceWatch.stop();
}
}
}
Here I use the Supplier
and IntConsumer
interface to provide externally with return value and no return worth calling. In the TraceHolder class, before and after the core code block, the previous TraceWatch
methods are respectively called to realize the time-consuming statistics function.
3.2 AutoCloseable
In addition to Function
the features used, we can also use the features of jdk 1.7 AutoCloseable
. It is said AutoCloseable
that some students may not have heard of it, but if you show the following code, you will immediately understand what it is.
// 未使用 AutoCloseable
public static String readFirstLingFromFile(String path) throws IOException {
BufferedReader br = null;
try {
br = new BufferedReader(new FileReader(path));
return br.readLine();
} catch (IOException e) {
e.printStackTrace();
} finally {
if (br != null) {
br.close();
}
}
return null;
}
// 使用 AutoCloseable
public static String readFirstLineFromFile(String path) throws IOException {
try (BufferedReader br = new BufferedReader(new FileReader(path))) {
return br.readLine();
}
}
In the try
back, you can load an AutoCloseable
object that implements the interface, which acts on the entire try
statement block, and calls back the AutoCloseable#close()
method after the execution is complete .
Let's modify the TraceWatch class:
Implement AutoCloseable
interface, implement close()
interface:
@Override
public void close() {
this.stop();
}
Modify the start()
method to support chain call:
public TraceWatch start(String taskName) throws IllegalStateException {
if (this.currentTaskName != null) {
throw new IllegalStateException("Can't start TraceWatch: it's already running");
}
this.currentTaskName = taskName;
this.startMs = TimeUtils.nowMs();
return this;
}
public class AutoCloseableTest {
public static void main(String[] args) {
TraceWatch traceWatch = new TraceWatch();
try(TraceWatch ignored = traceWatch.start("function1")) {
try {
TimeUnit.SECONDS.sleep(1); // 模拟业务代码
} catch (InterruptedException e) {
e.printStackTrace();
}
}
try(TraceWatch ignored = traceWatch.start("function2")) {
try {
TimeUnit.SECONDS.sleep(1); // 模拟业务代码
} catch (InterruptedException e) {
e.printStackTrace();
}
}
try(TraceWatch ignored = traceWatch.start("function1")) {
try {
TimeUnit.SECONDS.sleep(1); // 模拟业务代码
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(JSON.toJSONString(traceWatch.getTaskMap()));
}
}
/* output:
{"function2":[{"data":1001,"taskName":"function2"}],"function1":[{"data":1002,"taskName":"function1"},{"data":1002,"taskName":"function1"}]}
*/
Four, summary
This article lists four ways to count code time-consuming:
Time difference statistics
StopWatch
Function
AutoCloseable
The schemes listed are what I can think of at present.
There are hot recommendations???
19 pictures overview Spring Cloud
It is not difficult to realize SpringBoot's simple read-write separation from scratch!
Use VS Code to develop Spring Boot, Zhenni Ma show!
Stupidly indistinguishable Cookie, Session, Token, JWT
Dry goods|SpringBoot integrated Jiguang push complete implementation code (recommended collection)
Zhihu Gaozan: Which one to choose between Pinduoduo and State Grid Offer?
In-depth comparison of Jackson and Fastjson, in the end I still chose...
The ultimate guide to getting started with Docker, this is the best tutorial I have ever seen!
Click to read the original text, go to learn SpringCloud actual combat project