Java命令行应用程序以某种方式保留状态

3

前言:如果这是一个非常愚蠢或者已经有很好的文档记录的错误,我表示歉意。对我来说,现在它看起来非常奇怪,毫无意义。

应用程序

我有一个Java命令行应用程序,在macOS 10.13.4上使用IntelliJ IDEA Ultimate构建,利用下面列出的四个Maven库。它的目的是从网站下载文件,并在此过程中浏览分页结果。

该应用程序的一个特点是能够保持循环运行,如果足够的时间已经过去,它会检查新的结果。为了实现这一点,它在do-while块中作为while条件调用 Thread.sleep(remainingMillis)

问题

应用程序没有任何问题,但是在引入Thread.sleep()调用之后(我怀疑这是有问题的代码行),发生了一些非常奇怪的行为:应用程序在第一次运行时没有问题,从配置的网站获取了三个项目;然后将其配置为确保在再次运行之前已经过去了60秒。然而,在随后的运行中,日志显示它开始查看第31页(作为示例),在那里找不到结果。在未找到任何内容的情况下,第二次尝试查看第32页,最后一次尝试查看第33页;然后再次等待自扫描迭代开始以来已经过去了60秒。

我不能确认这一点,但似乎它在随后的扫描中继续计数:34、35,然后是36,再次等待。然而,代码应该表明当另一个while迭代开始时,这应该重新从1开始。

这可能是IntelliJ或Java出了问题,可能只需要清理bin/obj文件夹,但如果这是由于我的代码导致的,我更愿意知道它,这样我将来就不会遇到同样愚蠢的问题。

观察结果

仅仅几天后使用当前配置运行应用程序,意味着它不会调用Thread.sleep(),因为超过60秒,所以它立即继续下一次迭代;当这种情况发生时,奇怪的页面索引递增问题没有出现 - 相反,下一次迭代从页面1开始,正如它应该做的那样。

之后,运行它,使它在开始下一次迭代之前等待几秒钟的Thread.sleep()也没有引起问题......非常奇怪。这是一个梦吗?

代码

附注:我添加了Thread.currentThread().interrupt()来尝试解决这个问题,但似乎没有效果。

public static void main(String[] args) {
        do {
            startMillis = System.currentTimeMillis();
            int itemsFetched = startFetching(agent, config, record, 1, 0);
        } while (shouldRepeat(config.getRepeatSeconds(), startMillis));
    }

    private static boolean shouldRepeat(int repeatSeconds, long startMillis) {
        long passedMillis = System.currentTimeMillis() - startMillis;
        int repeatMillis = repeatSeconds * 1000;
        boolean repeatSecondsReached = passedMillis >= repeatMillis;

        if (repeatSeconds < 0) {
            return false;
        } else if (repeatSecondsReached) {
            return true;
        }

        long remainingMillis = repeatMillis - passedMillis;
        int remainingSeconds = (int) (remainingMillis / 1000);
        try {
            Thread.sleep(remainingMillis);
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            throw new RuntimeException(e);
        }

        return true;
    }

    private static int startFetching(Agenter agent, MyApplicationConfig config, MyApplicationRecord record, int pageIndex, int itemsFetched) {
        String categoryCode = config.getCategoryCode();
        List<Item> items = agent.getPageOfItems(categoryCode, pageIndex, config);

        if (items == null) {
            return itemsFetched;
        }

        int maxItems = config.getMaxItems();

        try {
            for (Item item : items) {
                String itemURL = item.getURL();

                agent.downloadItem(itemURL, config, item.getItemCount());
                itemsFetched++;

                if (maxItems > 0 && itemsFetched >= maxItems) {
                    return itemsFetched;
                }
            }
        } catch (IOException e) {
           // Log
        }

        return startFetching(agent, config, record, pageIndex + 1, itemsFetched);
    }
}

Maven库

  • commons-cli:commons-cli:1.4:用于解析命令行参数的工具库。
  • org.apache.logging.log4j:log4j-api:2.11.0:一个灵活且可扩展的日志框架,提供了强大的日志功能。
  • org.apache.logging.log4j:log4j-core:2.11.0:log4j-api的核心实现,提供了实际的日志记录和输出功能。
  • org.jsoup:jsoup:1.11.2:一款Java HTML解析器,可用于从HTML中提取数据。

你能否将此简化为最小测试用例,以便更容易阅读/诊断? - Oliver Charlesworth
我已经大幅缩短了代码。希望这更接近于你所寻找的内容。 - phantomraa
你有大量的日志记录,日志没有显示它使用的是哪个pageIndex吗?我猜测这可能与MyApplicationAgent类有关,该类可能存储了索引,在后续运行中不会被重置。 - Alim Özdemir
所以你正在一个递归方法上循环?这不奇怪吗? - Alexis Dufrenoy
@AlimÖzdemir 它确实有记录到控制台,但我现在无法检查,这就是我知道它在某个时候尝试访问31、32和33页的原因。startFetching方法接受一个pageIndex,每次顶层循环运行时都会将其设置为1。因此,我认为那不是问题所在,但我可能错了。 - phantomraa
@AlexisDufrenoy 或许有点奇怪,是的。我试图在控制流程上做些手腕,可能过于巧妙了。话虽如此,当下载足够的项目时,递归方法调用会自行结束并通过调用堆栈折叠回来,这就触发了下一个 do-while 迭代。下一个迭代将使用 pageIndex 为 1 调用 startFetching,这就是当它开始发送页面索引为 31 等的网络请求时让我感到困惑的原因。 - phantomraa
1个回答

0
请检查您的Agenter实现,在调用agent.getPageOfItems时,pageIndex被提供,但可能会在实例变量中存储或类似的地方。错误本身可能是因为在额外的调用中它可能没有被正确重置。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接