0

I have a multithreaded execution and I want to track and print out the execution time, but when I execute the code, the child threads takes longer than the main execution, thus the output is not visible nor it prints the right value, since it is terminating earlier.

Here is the code:

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);
}

I tried several solutions such as join(), wait() and notifyAll(), but none of them worked.

I found this Q&A on stackoverflow which treats my problem, but join() is ignored and if I put

es.awaitTermination(timeout, TimeUnit.SECONDS);

actually the executor service never executes threads.

Which can be the solution for executing multithreading only in ExecutorService block and finish with main execution at the end?

Community
  • 1
  • 1
Tsuneo
  • 43
  • 6
  • I would just submit the `Runnable` to the `ExecutorService` and not call `join()` or anything else. The way you are using the concurrency framework appears to be not correct. Also watch out that the spawned thread is a user thread: daemon threads will not stop the JVM from exiting. I am pretty sure that by default threads are user threads however. –  Apr 18 '12 at 02:50

2 Answers2

1

The ExecutorService#submit() method returns a Future object, which can be used for waiting until the submitted task has completed.

The idea is that you collect all of these Futures, and then call get() on each of them. This ensures that all of the submitted tasks have completed before your main thread continues.

Something like this:

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...
Chris B
  • 9,149
  • 4
  • 32
  • 38
1

Given your user case you might as well utilize the invokeAll method. From the Javadoc:

Executes the given tasks, returning a list of Futures holding their status and results when all complete. Future.isDone() is true for each element of the returned list. Note that a completed task could have terminated either normally or by throwing an exception. The results of this method are undefined if the given collection is modified while this operation is in progress.

To use:

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);

This will execute all supplied tasks and wait for them to finish processing before proceeding on your main thread. You will need to tweak the code to fit your particular needs, but you get the gist. A quick note, there is a variant of the invokeAll method that accepts timeout parameters. Use that variant if you want to wait up to a maximum amount of time before proceeding. And make sure to check the results collected after the invokeAll is done, in order to verify the status of the completed tasks.

Good luck.

Perception
  • 79,279
  • 19
  • 185
  • 195