我通过生成 5 个 pngout.exe 进程来优化 PNG 文件以处理 PNG 文件目录。由于 pngout 是单线程的,这会导致很大的加速。一些图像需要很长时间来优化,超过 30 秒,而标准是
代码:
private final ExecutorService pool = Executors.newFixedThreadPool(5);
/* ^ instance var, below is in method */
CompletionService<Boolean> comp = new ExecutorCompletionService<Boolean>(pool);
List<Callable<Boolean>> tasks = new ArrayList<Callable<Boolean>>();
for (int i = 0; i < files.length; i++) {
File infile = files[i];
File outfile = new File(outdir, infile.getName());
tasks.add(new CrushTask(crusher, infile, outfile));
}
for (Callable<Boolean> t : tasks)
comp.submit(t);
for (int i = 0; i < files.length; i++) {
try {
boolean res = comp.take().get();
System.out.println(res);
} catch (Exception e) {
e.printStackTrace();
}
}
所有文件都经过适当优化,这部分代码有效。问题在于,等待大图像后,整个过程会大大减慢。与单线程时间相比,我只得到了 40% 的改进。
我究竟做错了什么?
编辑:修复了这个问题,使用了一些非常难看的代码。问题是为了获得我正在生成的进程的退出值(知道它们何时完成以及它们是否成功),我正在将它们的标准输出读取为空,因为调用 waitFor 会永远挂起。但是,显然使用 InputStreams 会使线程窒息。
所以要获得进程的退出值,而不是使用这个:
private static int discardStdOut(Process proc) throws IOException {
final InputStream is = proc.getInputStream();
try {
while (is.read() != -1)
continue;
return proc.exitValue();
} finally {
close(is);
}
}
我正在使用这个粗略的代码:
private static int discardStdOut(Process proc) {
int ret = -1;
while (true) {
try {
ret = proc.exitValue();
break;
} catch (IllegalThreadStateException e) {
try {
Thread.sleep(100);
} catch (InterruptedException e2) {
e2.printStackTrace();
}
}
}
return ret;
}
这很糟糕,但现在系统运行良好,并且始终有 5 个进程在运行。
后期编辑:来自 here 的 StreamGobbler 可能更合适。
最佳答案
您正在遭受线程饥饿。您需要为 java 执行 sleep 或 IO 以正确管理线程。这不是 JVM 故障操作系统线程被破坏。
关于java - 长计算导致 ExecutorService 停止分配新工作,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/6566630/