上一讲【springboot源码解读系列】(二、springboot创建SpringApplication实例后,正式启动之前的准备工作)说到,springboot在run一开始就使用到了一个计时器,并且开始计时,那么我们今天看一下他这个计时器的作用吧。代码也就350行左右,但是功能确是非常多的,用起来很舒服,使用的是纳秒级别来进行计时的。
我们就具体来看看它是如何设计的吧。学学它的设计思想。
这个类是在org.springframework.util包下面的,是spring的一个工具包。我们经常使用spring,可以看一下他下面有哪些工具可以提供给我们使用。
/**
* 简单的秒表,允许对多个任务进行计时,公开总运行时间和每个指定任务的运行时间。
* 隐藏了System.nanoTime()的使用,提高了应用程序代码的可读性,减少了计算错误的可性。
* System.nanoTime()是纳米级别的统计时间
* 注意,此对象不是被设计为线程安全的,并且不使用同步,所以不适合在多线程下使用。
*
* 这个类通常用于在概念验证工作和开发中验证性能,而不是作为生产应用程序的一部分。
*
* 在Spring Framework 5.2中,运行时间以纳秒为单位进行跟踪和报告。
*
*/
public class StopWatch {
/**
* 秒表的标识符
*/
private final String id;
// 是否将任务保持起来,并且添加到taskList中,默认是为true的,
// 如果有成千上万个任务,那么这个就建议设置为false,任务太多,比较消耗性能
private boolean keepTaskList = true;
// 任务列表,这里使用的是LinkedList,是因为list要不停的增加任务,而LinkedList对于增删改速度非常快
private final List<TaskInfo> taskList = new LinkedList<>();
// 当前任务的启动时间。
private long startTimeNanos;
// 当前任务的名称
@Nullable
private String currentTaskName;
// 最后一个任务
@Nullable
private TaskInfo lastTaskInfo;
// 任务数
private int taskCount;
// 所有任务运行的事件
private long totalTimeNanos;
/**
* 构造一个秒表,不启动任何任务
*/
public StopWatch() {
this("");
}
/**
* 使用给定的ID构造一个新的秒表
* 这个id是用于输出的时候进行区别的
* 不启动任何任务。
*/
public StopWatch(String id) {
this.id = id;
}
/**
* 获取当前秒表的id
*/
public String getId() {
return this.id;
}
/**
* 配置taskInfo数组是否随时间构建。
*/
public void setKeepTaskList(boolean keepTaskList) {
this.keepTaskList = keepTaskList;
}
/**
* 启动一个未命名的任务。
* 如果在没有调用start方法,就去调用stop方法,那么当前的任务名称就会为null则报异常
* @see #start(String)
* @see #stop()
*/
public void start() throws IllegalStateException {
start("");
}
/**
* 启动一个命名的任务。
* 如果在没有调用start方法,就去调用stop方法,那么当前的任务名称就会为null则报异常
*/
public void start(String taskName) throws IllegalStateException {
// 判断上一个任务是否完成,如果上一个任务没有完成,则抛出异常
if (this.currentTaskName != null) {
throw new IllegalStateException("Can't start StopWatch: it's already running");
}
this.currentTaskName = taskName;
this.startTimeNanos = System.nanoTime();
}
/**
* 停止当前任务
*/
public void stop() throws IllegalStateException {
// 如果当前任务名称为null则抛出异常,因为没有任务在运行
if (this.currentTaskName == null) {
throw new IllegalStateException("Can't stop StopWatch: it's not running");
}
// 当前的时间减去任务开始的时间,就是当前任务的时间
long lastTime = System.nanoTime() - this.startTimeNanos;
// 将当前任务的时间累加到总任务时间上面
this.totalTimeNanos += lastTime;
// 根据停止的这条任务名和任务所花时间,创建一个任务实例,赋值给最后一个任务
this.lastTaskInfo = new TaskInfo(this.currentTaskName, lastTime);
// 判断保持任务list是否为true,默认为true,如果为true,则将任务添加到任务列表
if (this.keepTaskList) {
this.taskList.add(this.lastTaskInfo);
}
// 累加一次任务次数
++this.taskCount;
// 将当前任务名称设置为Null
this.currentTaskName = null;
}
/**
* 判断当前秒表是否在运行
*/
public boolean isRunning() {
return (this.currentTaskName != null);
}
/**
* 获取当前正在运行的任务名称
*
* @since 4.2.2
* @see #isRunning()
*/
@Nullable
public String currentTaskName() {
return this.currentTaskName;
}
/**
* 得到最后一次任务所消耗的时间的纳秒数,如果最后一次任务为null,则抛出异常
* @since 5.2
* @see #getLastTaskTimeMillis()
*/
public long getLastTaskTimeNanos() throws IllegalStateException {
if (this.lastTaskInfo == null) {
throw new IllegalStateException("No tasks run: can't get last task interval");
}
return this.lastTaskInfo.getTimeNanos();
}
/**
* 获取最后一次任务的毫秒数,如果最后一次任务为null,则抛出异常
* @see #getLastTaskTimeNanos()
*/
public long getLastTaskTimeMillis() throws IllegalStateException {
if (this.lastTaskInfo == null) {
throw new IllegalStateException("No tasks run: can't get last task interval");
}
return this.lastTaskInfo.getTimeMillis();
}
/**
* 获取最后一次任务的名称,如果最后一次任务为null,则抛出异常
*/
public String getLastTaskName() throws IllegalStateException {
if (this.lastTaskInfo == null) {
throw new IllegalStateException("No tasks run: can't get last task name");
}
return this.lastTaskInfo.getTaskName();
}
/**
* 获取最后一次任务对象,如果最后一次任务为null,则抛出异常
*/
public TaskInfo getLastTaskInfo() throws IllegalStateException {
if (this.lastTaskInfo == null) {
throw new IllegalStateException("No tasks run: can't get last task info");
}
return this.lastTaskInfo;
}
/**
* 获取所有的任务所执行的时间(纳秒级别的)
* @since 5.2
* @see #getTotalTimeMillis()
* @see #getTotalTimeSeconds()
*/
public long getTotalTimeNanos() {
return this.totalTimeNanos;
}
/**
* 获取所有的任务所执行的时间(毫秒级别的)
* @see #getTotalTimeNanos()
* @see #getTotalTimeSeconds()
*/
public long getTotalTimeMillis() {
return nanosToMillis(this.totalTimeNanos);
}
/**
* 获取所有的任务所执行的时间(秒级别的)
* @see #getTotalTimeNanos()
* @see #getTotalTimeMillis()
*/
public double getTotalTimeSeconds() {
return nanosToSeconds(this.totalTimeNanos);
}
/**
* 获取所有的任务数
*/
public int getTaskCount() {
return this.taskCount;
}
/**
* 获取所有的任务,如果keepTaskList为true的话,
* 如果keepTaskList为false,那么则报异常
*/
public TaskInfo[] getTaskInfo() {
if (!this.keepTaskList) {
throw new UnsupportedOperationException("Task info is not being kept!");
}
return this.taskList.toArray(new TaskInfo[0]);
}
/**
* 获取总运行时间的简短描述
*/
public String shortSummary() {
return "StopWatch '" + getId() + "': running time = " + getTotalTimeNanos() + " ns";
}
/**
* 输出所有任务的一些信息:名称,耗时等
*/
public String prettyPrint() {
// 这里使用StringBuilder,是因为当前类只适用于单线程,所以不考虑多线程
// 如果考虑到多线程,那么应该使用StringBuffer
StringBuilder sb = new StringBuilder(shortSummary());
sb.append('\n');
if (!this.keepTaskList) {
sb.append("No task info kept");
}
else {
sb.append("---------------------------------------------\n");
sb.append("ns % Task name\n");
sb.append("---------------------------------------------\n");
NumberFormat nf = NumberFormat.getNumberInstance();
nf.setMinimumIntegerDigits(9);
nf.setGroupingUsed(false);
NumberFormat pf = NumberFormat.getPercentInstance();
pf.setMinimumIntegerDigits(3);
pf.setGroupingUsed(false);
for (TaskInfo task : getTaskInfo()) {
sb.append(nf.format(task.getTimeNanos())).append(" ");
sb.append(pf.format((double) task.getTimeNanos() / getTotalTimeNanos())).append(" ");
sb.append(task.getTaskName()).append("\n");
}
}
return sb.toString();
}
/**
* 生成描述所执行任务的信息字符串
* 如果需要自定义报告,那么就调用getTaskInfo来进行自定义
*/
@Override
public String toString() {
StringBuilder sb = new StringBuilder(shortSummary());
if (this.keepTaskList) {
for (TaskInfo task : getTaskInfo()) {
sb.append("; [").append(task.getTaskName()).append("] took ").append(task.getTimeNanos()).append(" ns");
long percent = Math.round(100.0 * task.getTimeNanos() / getTotalTimeNanos());
sb.append(" = ").append(percent).append("%");
}
}
else {
sb.append("; no task info kept");
}
return sb.toString();
}
// 纳秒转毫秒
private static long nanosToMillis(long duration) {
return TimeUnit.NANOSECONDS.toMillis(duration);
}
// 纳秒转秒
private static double nanosToSeconds(long duration) {
// 1_000_000_000.0 等效于 1000000000,JDK1.7出的,
return duration / 1_000_000_000.0;
}
/**
* 内部任务类
*/
public static final class TaskInfo {
private final String taskName;
private final long timeNanos;
TaskInfo(String taskName, long timeNanos) {
this.taskName = taskName;
this.timeNanos = timeNanos;
}
/**
* 获取任务名称
*/
public String getTaskName() {
return this.taskName;
}
/**
* Get the time in nanoseconds this task took.
* @since 5.2
* @see #getTimeMillis()
* @see #getTimeSeconds()
*/
public long getTimeNanos() {
return this.timeNanos;
}
/**
* Get the time in milliseconds this task took.
* @see #getTimeNanos()
* @see #getTimeSeconds()
*/
public long getTimeMillis() {
return nanosToMillis(this.timeNanos);
}
/**
* Get the time in seconds this task took.
* @see #getTimeMillis()
* @see #getTimeNanos()
*/
public double getTimeSeconds() {
return nanosToSeconds(this.timeNanos);
}
}
}