Apache Lang3 StopWatch.split() 有什么意义?

What is the point of Apache Lang3 StopWatch.split()?

我目前正在评估 Apache StopWatch 和 Guava 的 Stopwatch 之间的实现,前者的拆分功能引起了我的兴趣,但我很难理解它到底做了什么,以及它有什么价值。

根据秒表的文档:https://commons.apache.org/proper/commons-lang/javadocs/api-3.9/org/apache/commons/lang3/time/StopWatch.html

split() the watch to get the time whilst the watch continues in the background. unsplit() will remove the effect of the split. At this point, these three options are available again.

而且我发现了一些例子,例如 this,它们提供的很少,因为它似乎只是累积的分裂。该页面说该方法用于“分割时间”,我根据该方法推测了很多,但该页面没有提及实际含义。甚至看起来这个例子是完全错误的,因为文档建议你应该 unsplitsplit 之前。

我最初认为它适用于以下用例:

StopWatch stopwatch = StopWatch.createStarted();
do something for 5 seconds
stopwatch.split();
do something for 10 seconds
stopwatch.stop();
System.out.println(stopwatch.getTime());
System.out.println(stopwatch.getSplitTime());

我原以为秒表总时间会读为 15 秒,秒表分段时间会读为 10 秒或 5 秒,但这两种方法似乎都输出 15 秒。

接下来,我想也许拆分值是一个增量,你可以取,然后从总计时器中删除,比如:

StopWatch stopwatch = StopWatch.createStarted();
do something for 5 seconds
stopwatch.split();
do something for 10 seconds
stopwatch.unsplit();
stopwatch.stop();
System.out.println(stopwatch.getTime());
// System.out.println(stopwatch.getSplitTime()); errors because of unsplit

我在这里的想法是拆分时间为 10 秒,当与主计时器分开时,主计时器将读取为 5 秒...但这似乎与 suspend() 调用没有什么不同...我也试过了,但时间对我来说仍然是一样的。

我是不是遗漏了什么,或者我对这应该做的事情的解释是错误的?

这是 getSplitTime()source code它在内部调用此其他函数):

public long getSplitNanoTime() {
  if (this.splitState != SplitState.SPLIT) {
     throw new IllegalStateException("Stopwatch must be split to get the split time. ");
  }
   return this.stopTime - this.startTime;
}

所以它会 return stopTime-startTime。当心stopTime是骗子骗你

这是 stop():

的代码
public void stop() {
  if (this.runningState != State.RUNNING && this.runningState != State.SUSPENDED) {
    throw new IllegalStateException("Stopwatch is not running. ");
  }
  if (this.runningState == State.RUNNING) 
  {
    //is this the same stopTime getSplitTime uses? yep, it is
     this.stopTime = System.nanoTime(); 
  }
  this.runningState = State.STOPPED;
}

那是怎么回事?

调用 stop() 更新 stopTime 变量并使秒表“忘记”上次拆分的时间。

split()stop() 都修改同一个变量,stopTime,当您在进程结束时调用 stop() 时,它会被覆盖。

虽然共享同一个变量可能看起来很奇怪,但这确实很有意义,因为 StopWatchsplittedTime 永远不应该大于总数经过的时间。所以这是一个关于 StopWatch.

中函数执行顺序的游戏

这是 split() 的代码,以查看这两种方法都使用 stopTime:

public void split() {
   if (this.runningState != State.RUNNING) {
      throw new IllegalStateException("Stopwatch is not running. ");
    }
    this.stopTime = System.nanoTime(); // you again little f..
    this.splitState = SplitState.SPLIT;
}

这就是为什么这个 可爱的 Apache 骗子 在 splittedTime 上显示 15 秒的原因:因为 stop() 更新了 stopTime 变量 getSplitTime() 将使用 return 它的价值。 (第一个代码片段)

注意 split() 函数的简单性(这也勉强回答了 OP 的问题)。它负责:

  1. 正在检查 StopWatch 是否为 运行.
  2. 标记一个新的 stopTime
  3. 正在将 splitState 设置为 SPLIT

                                      TLDR lol

在停止 StopWatch 之前调用 getSplitTime() 应该会显示所需的值:

  1. stopTime stop() 还不会更新。
  2. returning 值现在将匹配上次 split()startTime.
  3. 之间经过的时间

一些示例:(是的,周六晚上编辑因为我需要社交生活

StopWatch stopwatch = StopWatch.createStarted();
do something for 5 seconds
stopwatch.split(); //stopTime is updated [by split()]
System.out.println(stopwatch.getSplitTime()); // will show 5 seconds
do something for 10 seconds
System.out.println(stopwatch.getSplitTime()); // will also show 5 seconds
stopwatch.stop(); //stopTime is updated again [by stop()]
System.out.println(stopwatch.getTime()); // 15s
System.out.println(stopwatch.getSplitTime()); // 15s

另一个:

StopWatch stopwatch = StopWatch.createStarted();
do something for 5 seconds
stopwatch.split(); 
System.out.println(stopwatch.getSplitTime()); // 5s
do something for 10 seconds
stopwatch.split(); 
System.out.println(stopwatch.getSplitTime()); // 15s
do something for 1 second
stopwatch.stop();
System.out.println(stopwatch.getTime()); // 16s

还有最后一个。用 sleeps 嘲笑时间,只是为了好玩,你知道的。 我太无聊了我真的导入了 apache jar 以便在本地测试这个:

StopWatch stopwatch = StopWatch.createStarted();
Thread.sleep(5000);
stopwatch.split(); 
System.out.println(stopwatch.getSplitTime()); // 5s
Thread.sleep(10000);
stopwatch.split(); 
System.out.println(stopwatch.getSplitTime()); // 15s

stopwatch.reset();  // allows the stopWatch to be started again
stopwatch.start();  // updates startTime

Thread.sleep(2000);
stopwatch.split(); 
System.out.println(stopwatch.getSplitTime()); // 2s
Thread.sleep(1000);
stopwatch.stop();
System.out.println(stopwatch.getTime());      // 3s
System.out.println(stopwatch.getSplitTime()); // 3s
//it was so fun putting the sleeps

请注意,在停止的 Watch 上调用 getSplitTime() 不会抛出任何异常,因为该方法只会检查 splitStateSPLIT.

混淆可能是由这两个事实造成的:

  1. 该代码允许您 stop() 而不管 SplitState,使您最后的 split() 在您不知情的情况下徒劳无功。徒劳,我喜欢这个词。不得不以某种方式将其包含在我的回答中。 Futileeee
  2. 它还允许您在停止的手表上检查 splittedTime(如果它仍然处于 SPLIT 状态),当它真的会 return 之间的总经过时间最后 start() 和停止时间。 (小骗子)

在这种情况下,秒表同时停止和拆分getTime()getSplitTime()在调用时将始终显示相同的值在 stop().

之后

[个人主观意见]

假设您有一个 Counters class 和不同的变量来检查经过的时间。您还希望每 60 秒输出一次每个操作的总运行时间。在此示例中,countersCounters class 的一个实例,它拥有两个 long 变量:fileTimesendTime,将在特定时间间隔内(60s)累积每个操作的运行时间。 这只是一个示例,假设每次迭代花费的时间少于 1000 毫秒(因此经过的时间将始终显示 60 秒):

long statsTimer = System.currentTimeMillis();
while (mustWork)
{
    long elapsedStatsTimer = System.currentTimeMillis()-statsTimer; //hits 60185 
   
    if (elapsedStatsTimer  > 60000)
    {
       //counters.showTimes()
       System.out.println("Showing elapsed times for the last "+
                            (elapsedStatsTimer/1000)+ " secs"); //(60185/1000) - 60 secs
       System.out.println("Files time: "+counters.fileTime+" ms"); //23695 ms
       System.out.println("Send time : "+counters.sendTime+" ms"); //36280 ms          
       long workTime = counters.sendTime+counters.fileTime;
       System.out.println("Work time : "+workTime+" ms"); //59975 ms
       System.out.println("Others    : "+(elapsedStatsTimer-workTime)+" ms"); //210 ms
       //counters.reset()
       counters.fileTime=0;
       counters.sendTime=0;
       statsTimer= System.currentTimeMillis();
     }
     
     long timer = System.currentTimeMillis();
     //do something with a file
     counters.fileTime+=System.currentTimeMillis-timer;
     timer = System.currentTimeMillis();
     //send a message
     counters.sendTime+=System.currentTimeMillis()-timer;
}

Counters class 可以实现 reset()showTimes() 功能,以清理上面的代码。它还可以管理 elapsedStatsTimer 变量。这只是一个简化其行为的示例。

对于这个用例,其中你需要持续测量不同的操作,我认为这种方式更容易使用并且似乎具有相似的性能,因为 StopWatch 在内部做完全相同的事情。但是,嘿,这只是我的方式:)。

我将以光荣而徒劳的方式接受对此观点的反对票。

我很想以一分钟的沉默结束,以纪念 unsplit(),这可能是有史以来最无关紧要的方法之一。

[/个人主观意见]


刚刚注意到 TLDR 部分实际上比上一节大 :_ )