900字范文,内容丰富有趣,生活中的好帮手!
900字范文 > 使用Java实现三种精度的秒表(计时器)

使用Java实现三种精度的秒表(计时器)

时间:2023-07-22 14:45:42

相关推荐

使用Java实现三种精度的秒表(计时器)

1 引入

众所周知,链表和数组各有优缺点,其中增删改查操作速度就是显著差距之一,在学习ArrayListLinkedList时,我想用代码得到它们进行相同操作(如add(int index, E element)get(int index)等)时所耗时间的具体差距进一步证明它们的优缺点,所以此时就产生了一个秒表的必要性。(此秒表存在≤20ms的误差)

我想实现的效果:

public static void main(String[] args){秒表 sw = new 秒表();sw.开始计时();n行代码执行......sw.结束计时并返回计时结果();System.out.println("程序执行耗时="+计时结果)}

话不多说,开始一步一步制作这个Stopwatch类。

2 计时第一步——获取时间

在Java的System中提供了两种获取时间的方法——System.currentTimeMilis();System.nanoTime();

[关于这两种方法]1

public static long currentTimeMillis()

Returns the current time in milliseconds. Note that while the unit of time of the return value is a millisecond, the granularity of the value depends on the underlying operating system and may be larger. For example, many operating systems measure time in units of tens of milliseconds.

返回当前时间(以毫秒为单位)。 注意:虽然返回值的时间单位为毫秒,但该值的精度取决于底层操作系统,并且可能较大。 例如,许多操作系统以几十毫秒为单位测量时间。

返回值:

the difference, measured in milliseconds, between the current time and midnight, January 1, 1970 UTC.

返回由毫秒测量的从1970/1/1 UTC. 到 现在 UTC.的间隔(UTC.:协调世界时)2

public static long nanoTime()

Returns the current value of the running Java Virtual Machine’s high-resolution time source, in nanoseconds.

以纳秒为单位返回运行中的Java虚拟机高分辨率的当前时间

This method can only be used to measure elapsed time and is not related to any other notion of system or wall-clock time. The value returned represents nanoseconds since some fixed but arbitrary origin time (perhaps in the future, so values may be negative). The same origin is used by all invocations of this method in an instance of a Java virtual machine; other virtual machine instances are likely to use a different origin.

此方法只能用于测量一段与任何系统时间或钟表时间无关的经过时间。返回一个用纳秒表示的从一个固定但不确定的起始时间(可能在将来,所以返回的值可能是负数)。 在Java虚拟机中,该方法的所有调用都使用相同的来源; 其他虚拟机实例可能会使用不同的源。

This method provides nanosecond precision, but not necessarily nanosecond resolution (that is, how frequently the value changes) - no guarantees are made except that the resolution is at least as good as that of currentTimeMillis().

该方法提供纳秒精度,但分辨率不一定是纳秒(即值的变化频率) - 除了分辨率至少与currentTimeMillis()的分辨率不同之外,不作任何其他保证 。

Differences in successive calls that span greater than approximately 292 years (263 nanoseconds) will not correctly compute elapsed time due to numerical overflow.

跨越大约292年( 2 63 2^{63} 263纳秒)的差异将无法正确计算由于数字溢出所造成的时间。

The values returned by this method become meaningful only when the difference between two such values, obtained within the same instance of a Java virtual machine, is computed.

仅当在Java虚拟机的同一实例中获得的两个此类值之间的差异被计算时,此方法返回的值才会有意义。

返回值:

the current value of the running Java Virtual Machine’s high-resolution time source, in nanoseconds

运行中的Java虚拟机高分辨率的当前时间

3 计时第二步——得到结果

所以,我们至少可以实现两种不同精度的计时——纳秒级和毫秒级

计时思路:

记 录 开 始 时 间 → . . . → 记 录 结 束 时 间 记录开始时间 \to... \to 记录结束时间 记录开始时间→...→记录结束时间

此时结果:

( 结 束 时 间 − 开 始 时 间 ) → 进 率 转 换 \lparen 结束时间 - 开始时间 \rparen \to 进率转换 (结束时间−开始时间)→进率转换

而此时就可以进行思路扩展——加入暂停计时功能

计时思路:

记 录 开 始 时 间 → . . . → 记 录 暂 停 开 始 时 间 → . . . → 记 录 暂 停 结 束 时 间 → . . . → 记 录 结 束 时 间 记录开始时间\to...\to记录暂停开始时间\to...\to记录暂停结束时间\to...\to记录结束时间 记录开始时间→...→记录暂停开始时间→...→记录暂停结束时间→...→记录结束时间

此时结果:

{ 结 束 时 间 − [ 开 始 时 间 − ( 暂 停 开 始 时 间 − 暂 停 结 束 时 间 ) ] } → 进 率 转 换 \lbrace 结束时间 - [开始时间-(暂停开始时间-暂停结束时间)] \rbrace \to 进率转换 {结束时间−[开始时间−(暂停开始时间−暂停结束时间)]}→进率转换

4. 具体实现

4.1 秒表的状态

用枚举定义秒表的暂停,运行,停止的三种状态。

代码实现

mystop

package my_stopwatch.enums;public enum SWStatus {running,stopped,paused}

4.2 秒表运行流程

运行流程如下图所示:

4.3 秒表出现的异常

通过分析4.2中的图,我们可以发现,秒表中出现的异常有三种3(其他异常全部归结为“其他异常”统一处理)。我们可以如下表示

秒表的一个异常对象存储了抛出异常时传递的信息和抛出异常时记录到的时间。

代码实现:

my_stopwatch\Exceptions\SWException.java

package my_stopwatch.exceptions;public abstract class SWException extends RuntimeException{public SWException(String message, String currentTime) {super(message);this.currentTime = currentTime;}public String currentTime; //异常发生时计时器的时间@Overridepublic String toString() {return super.toString()+"\n异常发出者最后记录的时间:"+currentTime;}}}

my_stopwatch\Exceptions\SWAlreadyPausedException.java

package my_stopwatch.exceptions;public class SWAlreadyPausedException extends SWException{public SWAlreadyPausedException(String message, String currentTime) {super(message, currentTime);}}

my_stopwatch\Exceptions\SWAlreadyRunningException.java

package my_stopwatch.exceptions;public class SWAlreadyRunningException extends SWException{public SWAlreadyRunningException(String message, String currentTime) {super(message, currentTime);}}

my_stopwatch\Exceptions\SWAlreadyStoppedException.java

package my_stopwatch.exceptions;public class SWAlreadyStoppedException extends SWException{public SWAlreadyStoppedException(String message, String currentTime) {super(message, currentTime);}}

my_stopwatch\Exceptions\SWTimeOverflowException.java

package my_stopwatch.exceptions;public class SWTimeOverflowException extends SWException{public SWTimeOverflowException(String message, long currentTime) {super(message, currentTime);}}

4.4 秒表功能实现

以AbstractStopwatch作为三种不同精度的秒表的抽象父类,包含秒表要实现的方法并维护秒表需要的几个变量

秒表内部维护数值:

long sTime = -1L, //开始时间pSTime = 0L, //暂停开始时间timeVal = 0L; //秒表维护的计时结果SWStatus currentStatus = SWStatus.stopped; //计时器当前状态List<Long> pauseTimes = new LinkedList<>(); //秒表存储的所有暂停时间段

秒表实现的方法(摘AbstractStopwatch不完全代码):

/*** 读秒表* @param eTime 结束时间* @return 返回秒表当前时间*/public long Time(long eTime){...}/*** 读秒表字符串版* @param eTime 读秒表时时间* @param a 单位* @return 返回秒表当前时间的字符串版本*/public String Time(long eTime ,String a){...}/*** 开始计时* 当前状态为停止时,开始计时;当前状态为暂停时,强制开始新一轮计时。* @throws SWAlreadyRunningException 状态已为运行异常*/public abstract void startSW() throws SWAlreadyRunningException;/*** 停止计时* 当前状态为运行时,停止计时;当前状态为暂停时,强制停止计时。* @throws SWAlreadyStoppedException 当前状态已经为停止异常*/public abstract void stopSW() throws SWAlreadyStoppedException;/*** 继续计时* 当前状态为暂停,继续计时* @throws SWAlreadyRunningException 当前状态为运行,未暂停,异常* @throws SWAlreadyStoppedException 当前状态为停止,未开始计时,异常*/public abstract void continueSW() throws SWAlreadyRunningException,SWAlreadyStoppedException;/*** 暂停计时* 当前状态为运行,继续计时* @throws SWAlreadyPausedException 当前状态为暂停,异常* @throws SWAlreadyStoppedException 当前状态为停止,未开始计时,异常*/public abstract void pauseSW() throws SWAlreadyPausedException,SWAlreadyStoppedException;}

秒表执行流程和思路见4.2

代码实现:

my_stopwatch\swForms\AbstractStopwatch.java

package my_stopwatch.swForms;import my_stopwatch.enums.SWStatus;import my_stopwatch.exceptions.*;import java.util.LinkedList;import java.util.List;public abstract class AbstractStopwatch {long sTime = -1L, //开始时间pSTime = 0L, //暂停开始时间timeVal = 0L; //秒表维护的计时结果SWStatus currentStatus = SWStatus.stopped; //计时器当前状态List<Long> pauseTimes = new LinkedList<>(); //秒表存储的所有暂停时间段/*** 读秒表* @param eTime 结束时间* @return 返回秒表当前时间*/public long Time(long eTime){//计时结果 = 结束时间-开始时间-暂停时间timeVal = eTime-sTime;for(long i : pauseTimes){timeVal -= i;}return timeVal;}/*** 读秒表字符串版* @param eTime 读秒表时时间* @param a 单位* @return 返回秒表当前时间的字符串版本*/public String Time(long eTime ,String a){//通过普通读秒表方法计算计时结果再加上单位return ((Time(eTime))+a);}/*** 开始计时* 当前状态为停止时,开始计时;当前状态为暂停时,强制开始新一轮计时。* @throws SWAlreadyRunningException 状态已为运行异常*/public abstract void startSW() throws SWAlreadyRunningException;/*** 停止计时* 当前状态为运行时,停止计时;当前状态为暂停时,强制停止计时。* @throws SWAlreadyStoppedException 当前状态已经为停止异常*/public abstract void stopSW() throws SWAlreadyStoppedException;/*** 继续计时* 当前状态为暂停,继续计时* @throws SWAlreadyRunningException 当前状态为运行,未暂停,异常* @throws SWAlreadyStoppedException 当前状态为停止,未开始计时,异常*/public abstract void continueSW() throws SWAlreadyRunningException,SWAlreadyStoppedException;/*** 暂停计时* 当前状态为运行,继续计时* @throws SWAlreadyPausedException 当前状态为暂停,异常* @throws SWAlreadyStoppedException 当前状态为停止,未开始计时,异常*/public abstract void pauseSW() throws SWAlreadyPausedException,SWAlreadyStoppedException;}

my_stopwatch\swForms\MilliStopwatch.java

package my_stopwatch.swForms;import my_stopwatch.enums.SWStatus;import my_stopwatch.exceptions.*;public class MilliStopwatch extends AbstractStopwatch {public MilliStopwatch() {}/*** 读秒表* @return 返回秒表当前时间*/public long Time() {//直接调用父类方法得到结果return super.Time(System.currentTimeMillis());}/*** 读秒表字符串版* @param a 单位* @return 返回秒表当前时间的字符串版本*/public String Time(String a){//直接调用父类方法得到结果return super.Time(System.currentTimeMillis(), a);}/*** 开始计时* 当前状态为停止时,开始计时;当前状态为暂停时,强制开始新一轮计时。* @throws SWAlreadyRunningException 状态已为运行异常*/public void startSW() throws SWAlreadyRunningException {switch (currentStatus){//判断状态异常case running:throw new SWAlreadyRunningException("计时器已在运行!",Time(System.currentTimeMillis(),"ms"));default://记录计时开始时间并更新状态sTime = System.currentTimeMillis();currentStatus = SWStatus.running;//清空暂停时间段pauseTimes.clear();}}/*** 停止计时* 当前状态为运行时,停止计时;当前状态为暂停时,强制停止计时。* @throws SWAlreadyStoppedException 当前状态已经为停止异常*/public void stopSW() throws SWAlreadyStoppedException {switch (currentStatus){//判断状态异常case stopped:throw new SWAlreadyStoppedException("计时器已经停止了!",Time(System.currentTimeMillis(),"ms"));default://更新状态currentStatus = SWStatus.stopped;}}/*** 继续计时* 当前状态为暂停,继续计时* @throws SWAlreadyRunningException 当前状态为运行,未暂停,异常* @throws SWAlreadyStoppedException 当前状态为停止,未开始计时,异常*/public void continueSW() throws SWAlreadyRunningException, SWAlreadyStoppedException {switch (currentStatus){//判断状态异常case stopped:throw new SWAlreadyStoppedException("计时器未启动!",Time(System.currentTimeMillis(),"ms"));case running:throw new SWAlreadyRunningException("计时器已在运行!",Time(System.currentTimeMillis(),"ms"));default://将本段暂停时间加入所有暂停时间段列表pauseTimes.add(System.currentTimeMillis()-pSTime);//更新状态currentStatus = SWStatus.running;}}/*** 暂停计时* 当前状态为运行,继续计时* @throws SWAlreadyPausedException 当前状态为暂停,异常* @throws SWAlreadyStoppedException 当前状态为停止,未开始计时,异常*/public void pauseSW() throws SWAlreadyPausedException, SWAlreadyStoppedException {switch (currentStatus) {//判断状态异常case paused:throw new SWAlreadyPausedException("计时器已暂停!", Time(System.currentTimeMillis(), "ms"));case stopped:throw new SWAlreadyStoppedException("计时器未启动!", Time(System.currentTimeMillis(), "ms"));default://记录暂停开始时间并更新状态pSTime = System.currentTimeMillis();currentStatus = SWStatus.paused;}}}

my_stopwatch\swForms\NanoStopwatch.java

package my_stopwatch.swForms;import my_stopwatch.enums.*;import my_stopwatch.exceptions.*;public class NanoStopwatch extends AbstractStopwatch{public NanoStopwatch() {}/*** 读秒表* @return 返回秒表当前时间*/public long Time() {//直接调用父类方法得到结果return super.Time(System.nanoTime());}/*** 读秒表字符串版* @param a 单位* @return 返回秒表当前时间的字符串版本*/public String Time(String a){//直接调用父类方法得到结果return super.Time(System.nanoTime(), a);}/*** 开始计时* 当前状态为停止时,开始计时;当前状态为暂停时,强制开始新一轮计时。* @throws SWAlreadyRunningException 状态已为运行异常*/public void startSW() throws SWAlreadyRunningException {switch (currentStatus){//判断状态异常case running:throw new SWAlreadyRunningException("计时器已在运行!",Time(System.nanoTime(),"ns"));default://记录计时开始时间并更新状态sTime = System.nanoTime();currentStatus = SWStatus.running;//清空暂停时间段pauseTimes.clear();}}/*** 停止计时* 当前状态为运行时,停止计时;当前状态为暂停时,强制停止计时。* @throws SWAlreadyStoppedException 当前状态已经为停止异常*/public void stopSW() throws SWAlreadyStoppedException {switch (currentStatus){//判断状态异常case stopped:throw new SWAlreadyStoppedException("计时器已经停止了!",Time(System.nanoTime(),"ns"));default://更新状态currentStatus = SWStatus.stopped;}}/*** 继续计时* 当前状态为暂停,继续计时* @throws SWAlreadyRunningException 当前状态为运行,未暂停,异常* @throws SWAlreadyStoppedException 当前状态为停止,未开始计时,异常*/public void continueSW() throws SWAlreadyRunningException, SWAlreadyStoppedException {switch (currentStatus){//判断状态异常case stopped:throw new SWAlreadyStoppedException("计时器未启动!",Time(System.nanoTime(),"ns"));case running:throw new SWAlreadyRunningException("计时器已在运行!",Time(System.nanoTime(),"ns"));default://将本段暂停时间加入所有暂停时间段列表pauseTimes.add(System.nanoTime()-pSTime);//更新状态currentStatus = SWStatus.running;}}/*** 暂停计时* 当前状态为运行,继续计时* @throws SWAlreadyPausedException 当前状态为暂停,异常* @throws SWAlreadyStoppedException 当前状态为停止,未开始计时,异常*/public void pauseSW() throws SWAlreadyPausedException, SWAlreadyStoppedException {switch (currentStatus) {//判断状态异常case paused:throw new SWAlreadyPausedException("计时器已暂停!", Time(System.nanoTime(), "ns"));case stopped:throw new SWAlreadyStoppedException("计时器未启动!", Time(System.nanoTime(), "ns"));default://记录暂停开始时间并更新状态pSTime = System.nanoTime();currentStatus = SWStatus.paused;}}}

my_stopwatch\swForms\Stopwatch.java

package my_stopwatch.swForms;import my_stopwatch.enums.SWStatus;import my_stopwatch.exceptions.*;public class Stopwatch extends AbstractStopwatch{public Stopwatch() {}/*** 读秒表* @return 返回秒表当前时间*/public long Time() {//直接调用父类方法得到结果return super.Time((System.currentTimeMillis()/1000));}/*** 读秒表字符串版* @param a 单位* @return 返回秒表当前时间的字符串版本*/public String Time(String a){//直接调用父类方法得到结果return super.Time((System.currentTimeMillis()/1000), a);}/*** 开始计时* 当前状态为停止时,开始计时;当前状态为暂停时,强制开始新一轮计时。* @throws SWAlreadyRunningException 状态已为运行异常*/public void startSW() throws SWAlreadyRunningException {switch (currentStatus){//判断状态异常case running:throw new SWAlreadyRunningException("计时器已在运行!",Time((System.currentTimeMillis()/1000),"sec"));default://记录计时开始时间并更新状态sTime = (System.currentTimeMillis()/1000);currentStatus = SWStatus.running;//清空暂停时间段pauseTimes.clear();}}/*** 停止计时* 当前状态为运行时,停止计时;当前状态为暂停时,强制停止计时。* @throws SWAlreadyStoppedException 当前状态已经为停止异常*/public void stopSW() throws SWAlreadyStoppedException {switch (currentStatus){//判断状态异常case stopped:throw new SWAlreadyStoppedException("计时器已经停止了!",Time((System.currentTimeMillis()/1000),"sec"));default://更新状态currentStatus = SWStatus.stopped;}}/*** 继续计时* 当前状态为暂停,继续计时* @throws SWAlreadyRunningException 当前状态为运行,未暂停,异常* @throws SWAlreadyStoppedException 当前状态为停止,未开始计时,异常*/public void continueSW() throws SWAlreadyRunningException, SWAlreadyStoppedException {switch (currentStatus){//判断状态异常case stopped:throw new SWAlreadyStoppedException("计时器未启动!",Time((System.currentTimeMillis()/1000),"sec"));case running:throw new SWAlreadyRunningException("计时器已在运行!",Time((System.currentTimeMillis()/1000),"sec"));default://将本段暂停时间加入所有暂停时间段列表pauseTimes.add((System.currentTimeMillis()/1000)-pSTime);//更新状态currentStatus = SWStatus.running;}}/*** 暂停计时* 当前状态为运行,继续计时* @throws SWAlreadyPausedException 当前状态为暂停,异常* @throws SWAlreadyStoppedException 当前状态为停止,未开始计时,异常*/public void pauseSW() throws SWAlreadyPausedException, SWAlreadyStoppedException {switch (currentStatus) {//判断状态异常case paused:throw new SWAlreadyPausedException("计时器已暂停!", Time((System.currentTimeMillis()/1000), "sec"));case stopped:throw new SWAlreadyStoppedException("计时器未启动!", Time((System.currentTimeMillis()/1000), "sec"));default://记录暂停开始时间并更新状态pSTime = (System.currentTimeMillis()/1000);currentStatus = SWStatus.paused;}}}

5. 测试秒表

package my_stopwatch.test;import my_stopwatch.exceptions.SWException;import my_stopwatch.swForms.*;import java.util.concurrent.TimeUnit;public class TestSW {public static void doSomething(){try {TimeUnit.SECONDS.sleep(2);}catch (Exception e){}}public static void main(String[] args) {System.out.println("——————————————创建对象测试————————————\n");//NanoStopwatch SW = new NanoStopwatch(); String a = "ns";//MilliStopwatch SW = new MilliStopwatch(); String a = "ms";Stopwatch SW = new Stopwatch(); String a = "sec";System.out.println("————————————正常运行过程测试——————————\n");SW.startSW();System.out.println("计时开始");System.out.println("SW记录的时间:"+SW.Time(a+"\n"));doSomething();SW.pauseSW();System.out.println("计时暂停");System.out.println("SW记录的时间:"+SW.Time(a+"\n"));doSomething();SW.continueSW();System.out.println("计时继续");System.out.println("SW记录的时间:"+SW.Time(a+"\n"));doSomething();SW.stopSW();System.out.println("计时停止");System.out.println("SW记录的时间:"+SW.Time(a+"\n"));System.out.println("——————————————异常测试——————————————\n");try{SW.startSW();SW.startSW();}catch (SWException e){System.err.println(e.toString()+"\n");}try{SW.pauseSW();SW.pauseSW();}catch (SWException e){System.err.println(e.toString()+"\n");}try{SW.stopSW();SW.stopSW();}catch (SWException e){System.err.println(e.toString()+"\n");}}}

输出:

——————————————创建对象测试————————————————————————正常运行过程测试——————————计时开始SW记录的时间:0sec计时暂停SW记录的时间:2sec计时继续SW记录的时间:2sec计时停止SW记录的时间:4sec——————————————异常测试——————————————my_stopwatch.exceptions.SWAlreadyRunningException: 计时器已在运行!异常发出者最后记录的时间:0secmy_stopwatch.exceptions.SWAlreadyPausedException: 计时器已暂停!异常发出者最后记录的时间:0secmy_stopwatch.exceptions.SWAlreadyStoppedException: 计时器已经停止了!异常发出者最后记录的时间:0sec进程已结束,退出代码0

7 秒表误差

因为代码并不是瞬时执行的,所以此秒表可能有十几毫秒的误差。

节选并翻译自JavaAPI文档(个人翻译可能并不准确,请多多指正) ↩︎

计算机时间可能与真正的世界

协调时有差距 ↩︎

我们忽略时间溢出异常,因为就算是System.nanoTime();,它也因以long作为返回值类型支持长达约293年的计时,使用此异常可以忽略不计。 ↩︎

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。