完成ThreadPoolExecuter的所有任务后运行代码

时间:2015-09-09 04:47:09

标签: java multithreading

我有一个方法,我使用ThreadPoolExecuter创建一些文件,然后压缩创建的文件。

private void createAndZip(){
    // Some Code
    ThreadPoolExecutor executer = (ThreadPoolExecutor) Executors.newFixedThreadPool(5);
    for(String str : someStringList){
        // This piece of code creates files and drops to certain location.
        executer.execute(new MyRunnable());
    }
    executer.shutdown();
    // Code to Zip the files created above.
}

现在我创建zip文件的代码甚至在创建所有文件之前就已运行,因此并非所有文件都被压缩。

请帮忙。我试过睡眠,但不能保证创建文件需要多长时间。

4 个答案:

答案 0 :(得分:3)

您需要在执行程序对象上调用awaitTermination,以便等待执行程序完成关闭。

答案 1 :(得分:1)

在您的代码块中,您缩小了Executors.newFixedThreadPool(5)的回报范围。您有一个选择是使用它返回的ExecutorService。该类已经具有避免必须重新实现同步代码(如锁存器)的功能。例如:

使用期货

private void createAndZip(ExecutorService executor) throws ExecutionException, InterruptedException {
    // Some Code
    List<String> list = new ArrayList<>();
    // For a number of reasons ExecutorService should be constructed outside
    // ExecutorService executer = Executors.newFixedThreadPool(5);
    List<Future<?>> futures = new ArrayList<>();
    for(String str : list){
        // This piece of code creates files and drops to certain location.
        futures.add(executer.submit(new MyRunnable()));
    }
    // async work
    for (Future<?> future : futures) {
        future.get(); // blocks
    }

    // Code to Zip the files created above.
}

这里有一些优点:

  • 错误管理:如果您使用其他技术执行后台操作,则必须安排将错误从后台线程传递到主线程。在这里,未来会照顾到这一点。如果你的工作者抛出,那么异常会将它备份回你的控制线程。
  • 在代码中保留少量线程池。首先集中线程的原因是为了降低启动成本。如果您有任何大型程序,则只要您想并行执行操作,就不希望创建和销毁线程池。

使用Java8 Lambda,可以以更紧凑的方式编写循环。

分叉/加入

也许更适合您的任务,特别是如果您要处理文件树是Fork/Join framework。在这里,您可以将处理和压缩转换为提交到fork-join池的任务集合。这很简洁,因为你可以获得整个zip文件的Future,允许你从主线程中生成整个zip。与使用fork / join的设计类似的东西可能是:

static class PrepareFile extends RecursiveTask<Void> {

    private String filePath;

    PrepareFile(String filePath) {
        this.filePath = filePath;
    }

    @Override
    protected Void compute() {
        try {
            System.out.println(filePath);
            Thread.sleep(1009L);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        return null; // void
    }
}

static class ZipTask extends RecursiveTask<String>
{

    private List<String> files;

    ZipTask(List<String> files) {
        this.files = files;
    }

    @Override
    protected String compute() {
        List<PrepareFile> prepareTasks = new ArrayList<>();
        for(String file : files) {
            PrepareFile fileTask = new PrepareFile(file);
            prepareTasks.add(fileTask);
            fileTask.fork();
        }
        for(PrepareFile task : prepareTasks) {
            task.join(); // can collect results here
        }
        System.out.println("Zipping");
        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("Done task");
        return "filename.zip";
    }
}
public static void main(String[] args) {
    ForkJoinPool pool = new ForkJoinPool();
    List<String> toProcess = Arrays.asList("a","b");
    String filename = pool.invoke(new ZipTask(toProcess));
    System.out.println("Zipped " + filename);
}

这是一个你想要改变一些事情的例子,比如任务的返回类型以及如何调用任务。

在awaitTermination上

调用shutdown后可以使用awaitTermination方法等待所有进程终止。然而,在长时间运行的服务或程序中,这可能不是那么理想,其中线程池可以在操作之间共享。

答案 2 :(得分:1)

我认为你可以在这里使用Future个对象。而不是在执行程序上调用execute()使用submit()方法。这应该为您提交给执行者的每个任务提供一个Future对象。提交完所有任务后,只需循环查看所获得的期货清单,并在每个期货上调用get()。这是一个阻塞调用,它会一直等到相应的任务完成。

这样做的好处是,您可以检索从任务中抛出的任何异常,然后决定是否压缩文件。

请参阅此代码 -

private void createAndZip() throws Exception {
    // Some Code
    ThreadPoolExecutor executer = (ThreadPoolExecutor) Executors.newFixedThreadPool(5);
    // collect all futures
    List<Future> futures = new ArrayList<>();
    for(String str : someStringList){
        // This piece of code creates files and drops to certain location.
        futures.add(executer.submit(new MyRunnable()));
    }
    // wait for all tasks to finish
    try {
        for (Future future : futures) {
            future.get();
        }
    } catch (Exception e) {
        e.printStackTrace();
        if (e instanceof ExecutionException) {
            throw e;
        }
    } finally {
        executer.shutdown();
    }
    // Code to Zip the files created above.
}

答案 3 :(得分:0)

我使用CountDownLatch来解决问题。这是示例代码。

private void createAndZip() throws Exception{
    CountDownLatch latch = new CountDownLatch(someStringList.size());
    // Some Code
    ThreadPoolExecutor executer = (ThreadPoolExecutor) Executors.newFixedThreadPool(5);

    for(String str : someStringList){
        // This piece of code creates files and drops to certain location.
        executer.execute(new MyRunnable(latch));
    }
    executer.shutdown();


    // Code to Zip the files created above.
    try {
        latch.await();
    } catch (InterruptedException exception) {
        throw new GIException(exception);
    }

    //Code here.
}

public class MyRunnable implements Runnable{
    CountDownLatch latch = null;

    MyRunnable(CountDownLatch latch){
        this.latch = latch;
    }

    @Override
    public void run() {
        try {
            // Some Logic
            latch.countDown();
            } catch (Exception e) {
              e.printStackTrace();
            }
        }
    }