长计算会导致ExecutorService停止分配新工作

时间:2011-07-04 01:42:30

标签: java concurrency executorservice

我正在通过生成5个pngout.exe进程来优化PNG文件,以处理PNG文件的目录。由于pngout是单线程的,因此可以实现大幅加速。一些图像需要很长时间才能优化,超过30秒,而标准<5秒。问题:

  • 文件1很大,2-5很小,总共50个文件,但其余的细节无关紧要。
  • 前五个pngout进程正常生成并开始工作
  • 2-5秒内退出
  • 1需要45秒
  • 尽管有四个线程空闲
  • ,但在此期间不会产生新的pngout进程
  • 完成1后,会产生另外五个进程。

代码:

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%的改进。

我做错了什么?

编辑:使用一些非常难看的代码修复了这个问题。问题是要获得我正在产生的进程的退出值(知道它们何时完成以及它们是否成功)我正在读取它们的stdout,因为调用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可能更合适。

1 个答案:

答案 0 :(得分:0)

你正在获得线程饥饿。您需要为java进行睡眠或IO以正确管理线程。它不是JVMs故障操作系统的线程坏了。