【问题标题】:Java main class ends up before threads executionJava 主类在线程执行之前结束
【发布时间】:2012-04-18 00:20:10
【问题描述】:

我有一个多线程执行,我想跟踪并打印出执行时间,但是当我执行代码时,子线程比主执行花费更长的时间,因此输出不可见,也不会打印正确的值,因为它提前终止了。

代码如下:

public static void main(String[] args) throws CorruptIndexException, IOException, LangDetectException, InterruptedException {

    /* Initialization */
    long startingTime = System.currentTimeMillis();
    Indexer main = new Indexer(); // this class extends Thread
    File file = new File(SITES_PATH);
    main.addFiles(file);

    /* Multithreading through ExecutorService */
    ExecutorService es = Executors.newFixedThreadPool(4);
    for (File f : main.queue) {
        Indexer ind = new Indexer(main.writer, main.identificatore, f);
        ind.join();
        es.submit(ind);
    }

    es.shutdown();

    /* log creation - code I want to execute when all the threads execution ended */
    long executionTime = System.currentTimeMillis()-startingTime;
    long minutes = TimeUnit.MILLISECONDS.toMinutes(executionTime);
    long seconds = TimeUnit.MILLISECONDS.toSeconds(executionTime)%60;
    String fileSize = sizeConversion(FileUtils.sizeOf(file));

    Object[] array = {fileSize,minutes,seconds};
    logger.info("{} indexed in {} minutes and {} seconds.",array);
}

我尝试了几种解决方案,例如 join()、wait() 和 notifyAll(),但都没有奏效。

我在 stackoverflow 上找到了这个 Q&A,它处理了我的问题,但是 join() 被忽略了,如果我放了

es.awaitTermination(timeout, TimeUnit.SECONDS);

实际上执行器服务从不执行线程。

仅在 ExecutorService 块中执行多线程并在最后完成主执行的解决方案是什么?

【问题讨论】:

  • 我只会将Runnable 提交给ExecutorService,而不是调用join() 或其他任何东西。您使用并发框架的方式似乎不正确。还要注意生成的线程是用户线程:守护线程不会阻止 JVM 退出。我很确定默认情况下线程是用户线程。

标签: java multithreading


【解决方案1】:

鉴于您的用户案例,您不妨使用invokeAll 方法。来自 Javadoc:

执行给定的任务,返回持有它们的 Futures 列表 全部完成后的状态和结果。 Future.isDone() 对每个都是真的 返回列表的元素。请注意,已完成的任务可能有 正常终止或抛出异常。结果 如果给定集合被修改,则此方法未定义 此操作正在进行中。

使用方法:

final Collection<Indexer> tasks = new ArrayList<Indexer>();
for(final File f: main.queue) {
    tasks.add(new Indexer(main.writer, main.identificatore, f));
}

final ExecutorService es = Executors.newFixedThreadPool(4);
final List<Future<Object>> results = es.invokeAll(tasks);

这将执行所有提供的任务并等待它们完成处理,然后再继续主线程。您将需要调整代码以适应您的特定需求,但您明白了要点。快速说明,invokeAll 方法有一个变体,它接受超时参数。如果您想在继续之前等待最长时间,请使用该变体。并确保检查invokeAll完成后收集的结果,以验证已完成任务的状态。

祝你好运。

【讨论】:

    【解决方案2】:

    ExecutorService#submit() 方法返回一个Future 对象,可用于等待提交的任务完成。

    这个想法是你收集所有这些Futures,然后对它们每个调用get()。这可确保在您的主线程继续之前,所有提交的任务都已完成。

    类似这样的:

    ExecutorService es = Executors.newFixedThreadPool(4);
    List<Future<?>> futures = new ArrayList<Future<?>>();
    for (File f : main.queue) {
        Indexer ind = new Indexer(main.writer, main.identificatore, f);
        ind.join();
        Future<?> future = es.submit(ind);
        futures.add(future);
    }
    
    // wait for all tasks to complete
    for (Future<?> f : futures) {
        f.get();
    }
    
    // shutdown thread pool, carry on working in main thread...
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2018-04-27
      • 1970-01-01
      • 2015-08-10
      • 1970-01-01
      • 1970-01-01
      • 2019-04-03
      • 1970-01-01
      相关资源
      最近更新 更多