【springboot源码解读系列】(三、springboot启动使用的计时器StopWatch:短小精悍功能多,设计简单,容易阅读)

上一讲【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);
		}

	}

}

猜你喜欢

转载自blog.csdn.net/qq_42154259/article/details/107550516