运行配置有限制

时间:2019-05-15 18:37:41

标签: java

我对如何进行代码有疑问。 我的项目是在后台逐个运行配置的工具。我想为运行配置的数量增加一个限制。 例如,如果我有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的列表。

2 个答案:

答案 0 :(得分:8)

对不起 vesii ,如果我没有完全理解甚至误解您的问题,但是您的英语不是很好,即使您发表评论以查看使用多线程的问题所在,我也有问题

无论如何,我建议您使ConfigStruct类实现Runnable接口,这很容易,因为它已经具有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 ms。

现在,我们需要一个小的驱动程序应用程序,在其中生成配置并运行它们。解决您永远不能同时运行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 ms(13 x 100 ms)。
  • 在具有5个线程的线程池中运行时,运行时> 300 ms(3 x 100 ms)-正是您期望根据您的要求同时处理5个配置。
  • 由于使用多线程,因此日志输出不是直接的1到13,而是有些不同,这里最后是8、10、9、11、13、12。对于每个线程不同的处理时间,它看起来甚至会更多。

更新:如果您希望看到更多变化,只需在线程的睡眠时间中添加一个随机元素,然后将日志记录扩展一点:

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)?

还请注意,如果您在控制台上计算活动(未完成)线程的数量,则无论执行时间如何,该数量都不会超过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秒

答案 1 :(得分:-1)

听起来像是您在寻找java.util.concurrent.CountDownLatch。您给该数字(在您的情况下为5),然后等待一段时间使其达到0。Google将为您提供许多使用方法的示例。