0

I have a group of threads which all need to be executed in parallel and I must wait on all of them to complete.

Should I use the plain old Thread or the ExecutorService ? for the ExecutorService.awaitTermination I must give a certain time that I'm willing to wait but for Thread.join I must not.

I don't do anything with the results that the threads give , I don't need any futures.

EDIT:

    ExecutorService es = Executors.newFixedThreadPool(kThreads);
    List<Callable<Void>> calls = new LinkedList<>();

        container.forEach(
                calls.add(() -> { //creating a thread/task 
                    BufferedImage scaledBufferedImage=imageService.scale(...);

                    imageService.transferToAWS(...);
                    return null;
                })
        );
        es.invokeAll(calls); //executes each task
        es.shutdown(); //ensure that no new tasks will be accepted
        es.awaitTermination(Long.MAX_VALUE, TimeUnit.NANOSECONDS); //wait for all tasks to finish

return kLinksToTheScaledImages;
Oleg
  • 1,479
  • 3
  • 21
  • 42
  • 2
    If you don't do anything with the results then why do you need to wait at all?If you need to wait until they're done then you _will_ use the results in some way. – Thomas Nov 07 '17 at 12:21
  • 1
    Not really. You could let those threads do some kind of data processing and need to wait for them all to finish until you a) process the next batch of data. b) close the connection to a data source (not necessary a DB) c) can simply show a feedback to the user like "Done". I could continue with a few more examples, but the comment box is nearly full. – Korashen Nov 07 '17 at 12:24
  • What is the environment? Do you have a simple JavaSE program running on a local machine, or do you have an JavaEE environment with some kind of WebServer or ApplicationServer? – Korashen Nov 07 '17 at 12:25
  • How do you start your Threads? If you're using `ExecutorService#submit`, you won't have any `Thread` objects in your hand anyway. If you're manually creating `Thread`s (which you shouldn't), you can't really join them using a `ExecutorService`. I suggest you use the same mechanism for joining and forking. – daniu Nov 07 '17 at 12:25
  • @Korashen EE environment on a Webserver – Oleg Nov 07 '17 at 12:37
  • 1
    @Oleg In this case I strongly recomment to NOT use custom made threads, as the server will not know about them and they are running out of the servers context. Your webserver should provide you with threadpools, which you can configure via the Admin console. These threadpools are managed and watched by the server. You can get such a threadpool by a simple JNDI lookup or via the context. – Korashen Nov 07 '17 at 12:41
  • 1
    Try one of the following: https://docs.oracle.com/javaee/7/api/javax/enterprise/concurrent/ManagedExecutorService.html or https://www.javacodegeeks.com/2014/07/java-ee-concurrency-api-tutorial.html or just google for it, there is a bunch of different information about Threadpools on the net. – Korashen Nov 07 '17 at 12:51

5 Answers5

2

As you say, you don't need the Futures as such, but you can use them to await termination.

Since you're using Java 8, you can do this via

ExecutorService es = Executors.newFixedThreadPool(kThreads);
container.stream()
    .map(d -> createRunnable(d)) // now you have Runnables
    .map(es::submit) // now they're Futures
    .forEach(Future::get); // get() will wait for futures to finish

EDIT: I just realized that the stream will probably prevent the parallelism to begin with, so you need to collect them intermediately:

List<Future<?>> futures = container.stream()
    .map(d -> createRunnable(d)) // now you have Runnables
    .map(es::submit) // now they're Futures
    .collect(Collectors.toList());
futures.forEach(Future::get);

Actually, I fully understand that people are confused about having to wait without the futures returning a value. IMO, it would make more sense to have each future return the link of the upload it creates:

String scaleAndUpload() {
    BufferedImage scaledBufferedImage=imageService.scale(...);
    imageService.transferToAWS(...);
    return linkToUploadedImage;
}

So you would get something like

List<Future<?>> futures = container.stream()
    .map(d -> scaleAndUpload()) // now you have Runnables
    .map(es::submit) // now they're Futures
    .collect(Collectors.toList());
return futures.stream()
    .map(Future::get)  // collect the link when the future is finished
    .collect(Collectors.toList()); // create a list of them
daniu
  • 14,137
  • 4
  • 32
  • 53
1

go for the executor service. this gives you many other benefits on top of managing the thread termination (e.g. capacity scaling and separation of execution logic and the actual tasks)

light_303
  • 2,101
  • 2
  • 18
  • 35
1

With Executors you just do this.

ex.shutdown();
while (!ex.awaitTermination(1, TimeUnit.MINUTES)) {
}

Where ex is your ExecutorService. In the loop you can check if your threads are still alive or something like that.

1

Should I use the plain old Thread or the ExecutorService ?

Use ExecutorService

for the ExecutorService.awaitTermination I must give a certain time that I'm willing to wait but for Thread.join I must not.

For proper shutdown of ExecutorService:
How to properly shutdown java ExecutorService

A few more alternatives:
wait until all threads finish their work in java

philipxy
  • 14,867
  • 6
  • 39
  • 83
Ravindra babu
  • 37,698
  • 11
  • 250
  • 211
0

You can do while(!executor.isTerminated) {}. Then you don't have to say how long you are willing to wait.

Laxiwuka
  • 9
  • 1