如何在关机后重用线程池

时间:2011-10-11 07:14:33

标签: java concurrency

我有一个.csv文件,其中包含超过7000万行,每行生成一个 Runnable ,然后由threadpool执行。这个Runnable会在Mysql中插入一条记录。

更重要的是,我想记录 RandomAccessFile 的csv文件的位置以进行定位。该位置被写入文件。我想在线程池中的所有线程都完成时写入此记录。然后调用ThreadPoolExecutor.shutdown()。但是当更多的线路出现时,我又需要一个线程池。如何重用当前的线程池而不是重新创建新的线程池。

代码如下:

public static boolean processPage() throws Exception {

    long pos = getPosition();
    long start = System.currentTimeMillis();
    raf.seek(pos);
    if(pos==0)
        raf.readLine();
    for (int i = 0; i < PAGESIZE; i++) {
        String lineStr = raf.readLine();
        if (lineStr == null)
            return false;
        String[] line = lineStr.split(",");
        final ExperienceLogDO log = CsvExperienceLog.generateLog(line);
        //System.out.println("userId: "+log.getUserId()%512);

        pool.execute(new Runnable(){
            public void run(){
                try {
                    experienceService.insertExperienceLog(log);
                } catch (BaseException e) {
                    e.printStackTrace();
                }
            }
        });

        long end = System.currentTimeMillis();
    }

    BufferedWriter resultWriter = new BufferedWriter(
            new OutputStreamWriter(new FileOutputStream(new File(
                    RESULT_FILENAME), true)));
    resultWriter.write("\n");
    resultWriter.write(String.valueOf(raf.getFilePointer()));
    resultWriter.close();
    long time = System.currentTimeMillis()-start;
    System.out.println(time);
    return true;
}

谢谢!

3 个答案:

答案 0 :(得分:40)

documentation所述,您无法重复使用已关闭的ExecutorService。我建议不要使用任何变通办法,因为(a)它们可能无法在所有情况下按预期工作; (b)你可以使用标准类来达到你想要的效果。

你必须

  1. 实例化一个新的ExecutorService;或

  2. 不会终止ExecutorService

  3. 第一个解决方案很容易实现,所以我不会详细说明。

    对于第二个,由于您希望在所有提交的任务完成后执行操作,您可以查看ExecutorCompletionService并改为使用它。它包装了一个ExecutorService来进行线程管理,但是runnables会被包装成一些东西,告诉ExecutorCompletionService它们什么时候完成,所以它可以向你报告:

    ExecutorService executor = ...;
    ExecutorCompletionService ecs = new ExecutorCompletionService(executor);
    
    for (int i = 0; i < totalTasks; i++) {
      ... ecs.submit(...); ...
    }
    
    for (int i = 0; i < totalTasks; i++) {
      ecs.take();
    }
    

    take()类上的方法ExecutorCompletionService将阻塞,直到任务完成(正常或突然)。它将返回Future,因此您可以根据需要查看结果。

    我希望这可以帮到你,因为我没有完全理解你的问题。

答案 1 :(得分:3)

创建并分组所有任务,并使用invokeAll将其提交到池中(只有在所有任务成功完成后才会返回)

答案 2 :(得分:3)

在ExecutorService上调用shutdown后,no new Task will be accepted。这意味着您必须为每轮任务创建一个新的ExecutorService。

然而,随着Java 8 ForkJoinPool.awaitQuiescence被引入。如果您可以从普通的ExecutorService切换到ForkJoinPool,您可以使用此方法等待,直到ForkJoinPool中不再运行任务,而不必调用shutdown。这意味着您可以使用Tasks填充ForkJoinPool,等待它为空(静止),然后再次开始使用Tasks填充它,依此类推。