2 回答
TA贡献1963条经验 获得超6个赞
抱歉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 毫秒。
现在我们需要一个小的驱动程序应用程序,我们可以在其中生成配置并运行它们。解决你从不同时运行超过 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。对于每个线程的不同处理时间,它看起来会更加不同。
更新:如果你想看到更多变化,只需在线程的睡眠时间中添加一个随机元素并稍微扩展日志记录:
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 秒
TA贡献1921条经验 获得超9个赞
添加回答
举报