草庐IT

java - 有限制的运行配置

coder 2024-03-03 原文

我对如何继续我的代码有疑问。 我的项目是一个在后台一个一个地运行配置的工具。我想为运行配置的数量添加一个限制。 例如,如果我有 13 个配置,我想每次运行 5 个配置,那么顺序将是:

- Running 5 configurations
- All 5 configurations done running
- Running 5 configurations
- All 5 configurations done running
- Running 3 configurations
- All 3 configurations done running

目前的代码,工作如下:

public void runConfigurations(List<ConfigStruct> configurations) {
    for (ConfigStruct configuration : configurations) {
        try {
            configuration.run();
        } catch (ConfigurationException e) {
            continue;
        }
    }
}

现在,它会一个一个地运行每个配置。 run 方法如下所示:

public void run() throws ConfigurationException {
    StringBuffer runCmd = generateGalishFullCommand(GalishFlags.RUN);
    try {
        ExternalCommandExecutor.execute(runCmd, "Failed to run " + name, true, true);
    }  catch (IOException e) {
        throw new ConfigurationException(e.getMessage());
    }
}

execute 的签名如下所示:

public static String execute(final String cmd, final String error, final boolean runInBackground, final boolean retry) throws IOException;

起初,虽然我可以不在后台每 5 个配置运行最后一个配置,但它有问题。 我不能不在后台执行每 5 个配置的最后一个配置,因为第一个配置可能最后完成。 我该如何解决这个问题?

编辑:

当我打印配置时,它看起来如下:

[com.configStructs@3f15dbec, com.configStructs@31d2327e]

此外,configurationsconfigStructs 的列表。

最佳答案

抱歉 vesii 如果我没有完全理解甚至误解你的问题,但你的英语不是很好,即使在你的评论之后我也有问题,看看使用多线程的问题是什么.

无论如何,我建议你让你的 ConfigStruct 类实现 Runnable 接口(interface),这很容易,因为它已经有一个 run() 方法.您只需要摆脱抛出已检查的异常,因此我进一步建议将 ConfigurationException 设为 RuntimeException,您不必在方法签名中声明它。

很遗憾,您没有提供完整的 MCVE ,只有代码片段。所以我必须弥补其余部分才能编译和运行您的代码。我只是添加了一些简单的助手/虚拟类。我的解决方案如下所示:

package de.scrum_master.app;

public enum GalishFlags {
    RUN
}
package de.scrum_master.app;

public class ConfigurationException extends RuntimeException {
  private static final long serialVersionUID = 1L;

  public ConfigurationException(String message, Throwable cause) {
    super(message, cause);
  }
}
package de.scrum_master.app;

import java.io.IOException;

public class ExternalCommandExecutor {
  public static String execute(final String cmd, final String error, final boolean runInBackground, final boolean retry) throws IOException {
    System.out.println("Executing external command: " + cmd);
    try {
      Thread.sleep(100);
    } catch (InterruptedException e) {
      e.printStackTrace();
    }
    return cmd;
  }
}

如您所见,命令执行器在将内容打印到控制台后等待 100 毫秒。如果您希望程序运行得更慢或什至随机化以模拟需要不同时间完成的命令,您也可以将其更改为 1000 毫秒。

现在我们需要一个小的驱动程序应用程序,我们可以在其中生成配置并运行它们。解决永远不会同时运行超过 5 个线程的问题的关键是通过 Executors.newFixedThreadPool(5) 创建一个固定线程池。其余的应该很容易理解。

package de.scrum_master.app;

import java.io.IOException;

public class ConfigStruct implements Runnable {
  private String name;

  public ConfigStruct(String name) {
    this.name = name;
  }

  @Override
  public void run() {
    StringBuffer runCmd = generateGalishFullCommand(GalishFlags.RUN);
    try {
      ExternalCommandExecutor.execute(runCmd.toString(), "Failed to run " + name, true, true);
    } catch (IOException e) {
      throw new ConfigurationException(e.getMessage(), e);
    }
  }

  private StringBuffer generateGalishFullCommand(GalishFlags run) {
    return new StringBuffer("Galish full command for ConfigStruct '" + name + "'");
  }
}
package de.scrum_master.app;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

public class Application {
  public void runConfigurations(List<ConfigStruct> configurations) {
    for (ConfigStruct configuration : configurations) {
      try {
        configuration.run();
      } catch (ConfigurationException e) {
        continue;
      }
    }
  }

  public void runConfigurationsThreaded(List<ConfigStruct> configurations) {
    ExecutorService executorService = Executors.newFixedThreadPool(5);
    for (ConfigStruct configuration : configurations)
      executorService.execute(configuration);
    executorService.shutdown();
    try {
      executorService.awaitTermination(30, TimeUnit.SECONDS);
    } catch (InterruptedException e) {
      e.printStackTrace();
    }
  }

  public static void main(String[] args) {
    List<ConfigStruct> configurations = new ArrayList<>();
    for (int i = 1; i <= 13; i++)
      configurations.add(new ConfigStruct("Configuration " + i));

    long startTime = System.currentTimeMillis();
    new Application().runConfigurations(configurations);
    System.out.println("Total time (1 thread)  = " + (System.currentTimeMillis() - startTime) + " ms");
    System.out.println();

    startTime = System.currentTimeMillis();
    new Application().runConfigurationsThreaded(configurations);
    System.out.println("Total time (5 threads) = " + (System.currentTimeMillis() - startTime) + " ms");
  }
}

控制台日志看起来像这样:

Executing external command: Galish full command for ConfigStruct 'Configuration 1'
Executing external command: Galish full command for ConfigStruct 'Configuration 2'
Executing external command: Galish full command for ConfigStruct 'Configuration 3'
Executing external command: Galish full command for ConfigStruct 'Configuration 4'
Executing external command: Galish full command for ConfigStruct 'Configuration 5'
Executing external command: Galish full command for ConfigStruct 'Configuration 6'
Executing external command: Galish full command for ConfigStruct 'Configuration 7'
Executing external command: Galish full command for ConfigStruct 'Configuration 8'
Executing external command: Galish full command for ConfigStruct 'Configuration 9'
Executing external command: Galish full command for ConfigStruct 'Configuration 10'
Executing external command: Galish full command for ConfigStruct 'Configuration 11'
Executing external command: Galish full command for ConfigStruct 'Configuration 12'
Executing external command: Galish full command for ConfigStruct 'Configuration 13'
Total time (1 thread)  = 1374 ms

Executing external command: Galish full command for ConfigStruct 'Configuration 1'
Executing external command: Galish full command for ConfigStruct 'Configuration 2'
Executing external command: Galish full command for ConfigStruct 'Configuration 3'
Executing external command: Galish full command for ConfigStruct 'Configuration 4'
Executing external command: Galish full command for ConfigStruct 'Configuration 5'
Executing external command: Galish full command for ConfigStruct 'Configuration 6'
Executing external command: Galish full command for ConfigStruct 'Configuration 7'
Executing external command: Galish full command for ConfigStruct 'Configuration 8'
Executing external command: Galish full command for ConfigStruct 'Configuration 10'
Executing external command: Galish full command for ConfigStruct 'Configuration 9'
Executing external command: Galish full command for ConfigStruct 'Configuration 11'
Executing external command: Galish full command for ConfigStruct 'Configuration 13'
Executing external command: Galish full command for ConfigStruct 'Configuration 12'
Total time (5 threads) = 344 ms

请注意:

  • 在单线程循环中运行时,运行时间 > 1,300 毫秒(13 x 100 毫秒)。
  • 在具有 5 个线程的线程池中运行时,运行时间 > 300 毫秒(3 x 100 毫秒)- 根据同时处理 5 个配置的要求,这正是您所期望的。
  • 由于多线程,日志输出不是直接从 1 到 13,而是有点不同,这里最后是 8、10、9、11、13、12。对于每个线程的不同处理时间,它看起来会更加不同。

更新:如果您想看到更多变化,只需在线程的 hibernate 时间中添加一个随机元素并稍微延长日志记录:

package de.scrum_master.app;

import java.io.IOException;
import java.util.Random;

public class ExternalCommandExecutor {
  private static final Random RANDOM = new Random();

  public static String execute(final String cmd, final String error, final boolean runInBackground, final boolean retry) throws IOException {
    long sleepTime = 100 + 100 * (RANDOM.nextInt(3));
    System.out.println("Executing external command: " + cmd + ", sleeping for " + sleepTime + " ms");
    try {
      Thread.sleep(sleepTime);
    } catch (InterruptedException e) {
      e.printStackTrace();
    }
    System.out.println("Finished execution: " + cmd);
    return cmd;
  }
}

然后控制台日志可能如下所示:

Executing external command: Galish full command for ConfigStruct 'Configuration 1', sleeping for 300 ms
Finished execution: Galish full command for ConfigStruct 'Configuration 1'
Executing external command: Galish full command for ConfigStruct 'Configuration 2', sleeping for 100 ms
Finished execution: Galish full command for ConfigStruct 'Configuration 2'
Executing external command: Galish full command for ConfigStruct 'Configuration 3', sleeping for 200 ms
Finished execution: Galish full command for ConfigStruct 'Configuration 3'
Executing external command: Galish full command for ConfigStruct 'Configuration 4', sleeping for 300 ms
Finished execution: Galish full command for ConfigStruct 'Configuration 4'
Executing external command: Galish full command for ConfigStruct 'Configuration 5', sleeping for 100 ms
Finished execution: Galish full command for ConfigStruct 'Configuration 5'
Executing external command: Galish full command for ConfigStruct 'Configuration 6', sleeping for 100 ms
Finished execution: Galish full command for ConfigStruct 'Configuration 6'
Executing external command: Galish full command for ConfigStruct 'Configuration 7', sleeping for 200 ms
Finished execution: Galish full command for ConfigStruct 'Configuration 7'
Executing external command: Galish full command for ConfigStruct 'Configuration 8', sleeping for 200 ms
Finished execution: Galish full command for ConfigStruct 'Configuration 8'
Executing external command: Galish full command for ConfigStruct 'Configuration 9', sleeping for 200 ms
Finished execution: Galish full command for ConfigStruct 'Configuration 9'
Executing external command: Galish full command for ConfigStruct 'Configuration 10', sleeping for 100 ms
Finished execution: Galish full command for ConfigStruct 'Configuration 10'
Executing external command: Galish full command for ConfigStruct 'Configuration 11', sleeping for 200 ms
Finished execution: Galish full command for ConfigStruct 'Configuration 11'
Executing external command: Galish full command for ConfigStruct 'Configuration 12', sleeping for 200 ms
Finished execution: Galish full command for ConfigStruct 'Configuration 12'
Executing external command: Galish full command for ConfigStruct 'Configuration 13', sleeping for 100 ms
Finished execution: Galish full command for ConfigStruct 'Configuration 13'
Total time (1 thread)  = 2314 ms

Executing external command: Galish full command for ConfigStruct 'Configuration 1', sleeping for 300 ms
Executing external command: Galish full command for ConfigStruct 'Configuration 2', sleeping for 300 ms
Executing external command: Galish full command for ConfigStruct 'Configuration 3', sleeping for 200 ms
Executing external command: Galish full command for ConfigStruct 'Configuration 5', sleeping for 300 ms
Executing external command: Galish full command for ConfigStruct 'Configuration 4', sleeping for 100 ms
Finished execution: Galish full command for ConfigStruct 'Configuration 4'
Executing external command: Galish full command for ConfigStruct 'Configuration 6', sleeping for 200 ms
Finished execution: Galish full command for ConfigStruct 'Configuration 3'
Executing external command: Galish full command for ConfigStruct 'Configuration 7', sleeping for 200 ms
Finished execution: Galish full command for ConfigStruct 'Configuration 1'
Finished execution: Galish full command for ConfigStruct 'Configuration 2'
Executing external command: Galish full command for ConfigStruct 'Configuration 8', sleeping for 200 ms
Executing external command: Galish full command for ConfigStruct 'Configuration 9', sleeping for 100 ms
Finished execution: Galish full command for ConfigStruct 'Configuration 5'
Executing external command: Galish full command for ConfigStruct 'Configuration 10', sleeping for 200 ms
Finished execution: Galish full command for ConfigStruct 'Configuration 6'
Executing external command: Galish full command for ConfigStruct 'Configuration 11', sleeping for 200 ms
Finished execution: Galish full command for ConfigStruct 'Configuration 9'
Finished execution: Galish full command for ConfigStruct 'Configuration 7'
Executing external command: Galish full command for ConfigStruct 'Configuration 12', sleeping for 200 ms
Executing external command: Galish full command for ConfigStruct 'Configuration 13', sleeping for 200 ms
Finished execution: Galish full command for ConfigStruct 'Configuration 8'
Finished execution: Galish full command for ConfigStruct 'Configuration 10'
Finished execution: Galish full command for ConfigStruct 'Configuration 11'
Finished execution: Galish full command for ConfigStruct 'Configuration 13'
Finished execution: Galish full command for ConfigStruct 'Configuration 12'
Total time (5 threads) = 609 ms

看看在单线程模式下如何仍然一切都是 FIFO(先进先出)?

另请注意,如果您计算控制台上 Activity (未完成)线程的数量,无论执行时间如何,它都不会超过 5。最后 5 个线程结束。而且总执行时间仍然明显小于单线程情况。


更新 2: 最后但同样重要的是,如果将主循环中的元素数量从 13 增加到更大的数字,比如 100,您会注意到最后的总执行时间多线程解决方案的大约是单线程解决方案的 1/5(或通常 1 除以固定线程池中的线程数)。这是因为线程除了等待并打印到控制台外没有做太多其他事情。如果他们实际上做了更多的工作,例如繁重的计算或大量的 I/O,那么改进将不那么显着,但仍然很重要。

我对 100 个配置元素的尝试产生了以下输出(缩写):

Executing external command: Galish full command for ConfigStruct 'Configuration 1', sleeping for 300 ms
Finished execution: Galish full command for ConfigStruct 'Configuration 1'
(...)
Executing external command: Galish full command for ConfigStruct 'Configuration 100', sleeping for 300 ms
Finished execution: Galish full command for ConfigStruct 'Configuration 100'
Total time (1 thread)  = 20355 ms

Executing external command: Galish full command for ConfigStruct 'Configuration 2', sleeping for 100 ms
Executing external command: Galish full command for ConfigStruct 'Configuration 1', sleeping for 300 ms
(...)
Executing external command: Galish full command for ConfigStruct 'Configuration 100', sleeping for 200 ms
Finished execution: Galish full command for ConfigStruct 'Configuration 99'
Finished execution: Galish full command for ConfigStruct 'Configuration 93'
Finished execution: Galish full command for ConfigStruct 'Configuration 94'
Finished execution: Galish full command for ConfigStruct 'Configuration 95'
Finished execution: Galish full command for ConfigStruct 'Configuration 100'
Total time (5 threads) = 3923 ms

看到了吗? ~20 秒/5 = ~4 秒

关于java - 有限制的运行配置,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56155737/

有关java - 有限制的运行配置的更多相关文章

  1. ruby - 如何从 ruby​​ 中的字符串运行任意对象方法? - 2

    总的来说,我对ruby​​还比较陌生,我正在为我正在创建的对象编写一些rspec测试用例。许多测试用例都非常基础,我只是想确保正确填充和返回值。我想知道是否有办法使用循环结构来执行此操作。不必为我要测试的每个方法都设置一个assertEquals。例如:describeitem,"TestingtheItem"doit"willhaveanullvaluetostart"doitem=Item.new#HereIcoulddotheitem.name.shouldbe_nil#thenIcoulddoitem.category.shouldbe_nilendend但我想要一些方法来使用

  2. ruby - 如何每月在 Heroku 运行一次 Scheduler 插件? - 2

    在选择我想要运行操作的频率时,唯一的选项是“每天”、“每小时”和“每10分钟”。谢谢!我想为我的Rails3.1应用程序运行调度程序。 最佳答案 这不是一个优雅的解决方案,但您可以安排它每天运行,并在实际开始工作之前检查日期是否为当月的第一天。 关于ruby-如何每月在Heroku运行一次Scheduler插件?,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.com/questions/8692687/

  3. ruby-on-rails - 如何在 ruby​​ 中使用两个参数异步运行 exe? - 2

    exe应该在我打开页面时运行。异步进程需要运行。有什么方法可以在ruby​​中使用两个参数异步运行exe吗?我已经尝试过ruby​​命令-system()、exec()但它正在等待过程完成。我需要用参数启动exe,无需等待进程完成是否有任何ruby​​gems会支持我的问题? 最佳答案 您可以使用Process.spawn和Process.wait2:pid=Process.spawn'your.exe','--option'#Later...pid,status=Process.wait2pid您的程序将作为解释器的子进程执行。除

  4. ruby - 无法运行 Rails 2.x 应用程序 - 2

    我尝试运行2.x应用程序。我使用rvm并为此应用程序设置其他版本的ruby​​:$rvmuseree-1.8.7-head我尝试运行服务器,然后出现很多错误:$script/serverNOTE:Gem.source_indexisdeprecated,useSpecification.Itwillberemovedonorafter2011-11-01.Gem.source_indexcalledfrom/Users/serg/rails_projects_terminal/work_proj/spohelp/config/../vendor/rails/railties/lib/r

  5. ruby-on-rails - 独立 ruby​​ 脚本的配置文件 - 2

    我有一个在Linux服务器上运行的ruby​​脚本。它不使用rails或任何东西。它基本上是一个命令行ruby​​脚本,可以像这样传递参数:./ruby_script.rbarg1arg2如何将参数抽象到配置文件(例如yaml文件或其他文件)中?您能否举例说明如何做到这一点?提前谢谢你。 最佳答案 首先,您可以运行一个写入YAML配置文件的独立脚本:require"yaml"File.write("path_to_yaml_file",[arg1,arg2].to_yaml)然后,在您的应用中阅读它:require"yaml"arg

  6. ruby - Sinatra:运行 rspec 测试时记录噪音 - 2

    Sinatra新手;我正在运行一些rspec测试,但在日志中收到了一堆不需要的噪音。如何消除日志中过多的噪音?我仔细检查了环境是否设置为:test,这意味着记录器级别应设置为WARN而不是DEBUG。spec_helper:require"./app"require"sinatra"require"rspec"require"rack/test"require"database_cleaner"require"factory_girl"set:environment,:testFactoryGirl.definition_file_paths=%w{./factories./test/

  7. java - 等价于 Java 中的 Ruby Hash - 2

    我真的很习惯使用Ruby编写以下代码:my_hash={}my_hash['test']=1Java中对应的数据结构是什么? 最佳答案 HashMapmap=newHashMap();map.put("test",1);我假设? 关于java-等价于Java中的RubyHash,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.com/questions/22737685/

  8. Ruby Sinatra 配置用于生产和开发 - 2

    我已经在Sinatra上创建了应用程序,它代表了一个简单的API。我想在生产和开发上进行部署。我想在部署时选择,是开发还是生产,一些方法的逻辑应该改变,这取决于部署类型。是否有任何想法,如何完成以及解决此问题的一些示例。例子:我有代码get'/api/test'doreturn"Itisdev"end但是在部署到生产环境之后我想在运行/api/test之后看到ItisPROD如何实现? 最佳答案 根据SinatraDocumentation:EnvironmentscanbesetthroughtheRACK_ENVenvironm

  9. ruby-on-rails - 无法让 rspec、spork 和调试器正常运行 - 2

    GivenIamadumbprogrammerandIamusingrspecandIamusingsporkandIwanttodebug...mmm...let'ssaaay,aspecforPhone.那么,我应该把“require'ruby-debug'”行放在哪里,以便在phone_spec.rb的特定点停止处理?(我所要求的只是一个大而粗的箭头,即使是一个有挑战性的程序员也能看到:-3)我已经尝试了很多位置,除非我没有正确测试它们,否则会发生一些奇怪的事情:在spec_helper.rb中的以下位置:require'rubygems'require'spork'

  10. java - 从 JRuby 调用 Java 类的问题 - 2

    我正在尝试使用boilerpipe来自JRuby。我看过guide从JRuby调用Java,并成功地将它与另一个Java包一起使用,但无法弄清楚为什么同样的东西不能用于boilerpipe。我正在尝试基本上从JRuby中执行与此Java等效的操作:URLurl=newURL("http://www.example.com/some-location/index.html");Stringtext=ArticleExtractor.INSTANCE.getText(url);在JRuby中试过这个:require'java'url=java.net.URL.new("http://www

随机推荐