【问题标题】:What is the point of Apache Lang3 StopWatch.split()?Apache Lang3 StopWatch.split() 的意义何在?
【发布时间】:2020-10-29 10:55:34
【问题描述】:

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

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

split() 手表在后台继续运行时获取时间。 unsplit() 将删除拆分的效果。此时,这三个选项再次可用。

我发现了一些例子,例如this,它们提供的很少,因为它看起来分裂只是累积的。该页面说该方法用于“分割时间”,我根据该方法得出了同样多的结论,但该页面没有提及这实际上意味着什么。甚至看起来这个例子是完全错误的,因为文档建议你应该先unsplit,然后再split

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

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() 调用没有什么不同...我也试过这个,但对我来说时间还是一样的。

我在这里遗漏了什么,还是我对这应该做的事情的解释全错了?

【问题讨论】:

    标签: java guava apache-commons apache-commons-lang3


    【解决方案1】:

    这是 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;
    }
    

    所以它会返回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() 将用于返回其值。 (第一个代码sn-p)

    注意split() 函数的简单性(这也几乎没有回答 OP 的问题)。它负责:

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

                                          TLDR lol
    

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

    1. stopTime不会被stop()更新。
    2. 返回值现在将匹配最后一个 split()startTime 之间经过的时间。

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

    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() 不会引发任何异常,因为该方法只会检查 splitState 是否为 SPLIT.

    混淆可能是由以下两个事实引起的:

    1. 无论SplitState如何,该代码都允许您stop(),让您的最后一个split()在您不知情的情况下徒劳无功。徒劳的,我喜欢这个词。不得不以某种方式将其包含在我的答案中。 无用的人
    2. 它还允许您在停止的手表上检查splittedTime(如果它仍处于SPLIT 状态),它实际上只会返回最后一个start() 和停止时间之间的总经过时间。 (小骗子)

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


    [个人和主观意见]

    假设您有一个 Counters 类,其中包含不同的变量来检查经过的时间。您还希望每 60 秒输出每个操作的总经过时间。在此示例中,countersCounters 类的一个实例,该类拥有两个 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 类可以实现reset()showTimes() 函数,以便清理上面的代码。它还可以管理elapsedStatsTimer 变量。这只是一个简化其行为的示例。

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

    我将以一种光荣而徒劳的方式接受对这一意见的反对票。

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

    [/个人和主观意见]


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

    【讨论】:

    • 感谢您的详尽回答。是的,我正在阅读源代码,它只是在调用 stop() 之前没有点击我来检查拆分。在发布这个问题之后,我确实想在 stop() 之前调用 getSplitTime() 一段时间,但我不知道当时我是否已经掌握了所有的部分(当时试图连接这些点,A++ KISS 被逼疯了)对我而言)。你的解释加强了它。在这一点上,我能看到从中获得的唯一真正有价值的东西是分析一系列顺序操作。即OP A,A的拆分校验时间,unsplit,OP B,拆分,B的校验时间。
    猜你喜欢
    • 1970-01-01
    • 2021-05-20
    • 1970-01-01
    • 2013-12-07
    • 1970-01-01
    • 2018-04-04
    • 2019-04-23
    • 2018-10-17
    • 2016-05-17
    相关资源
    最近更新 更多