1

How can I hold the main thread until all 5 tasks(threads) have been completed?

class ReadMessages {

    private final ExecutorService executorService = Executors.newFixedThreadPool(5);

void readMessage(List<Messages> msg ) 
{
   CountDownLatch latch = new CountDownLatch(msg.size); /// lets say msg.size()=5

           for( Messages m : msg) {
                executorService.submit(() -> dbservice.processInDB(message)); //execute saveInDb paaralllely in 5 different threads 
            }

           //Hold the main thread until all 5 threads have completed their work. i.e make latch count to 0 
           
           //then send email
           emailService();
        }
Software Engineer
  • 15,457
  • 7
  • 74
  • 102
Pale Blue Dot
  • 511
  • 2
  • 13
  • 33
  • 2
    I think you're looking for [CountDownLatch#await](https://docs.oracle.com/javase/7/docs/api/java/util/concurrent/CountDownLatch.html#await()). Your submitted tasks will need to actually count down the latch though. – Marc Baumbach Jul 23 '21 at 20:57
  • @MarcBaumbach I tried using it but its not executing properly.Can you modify my code or where should i put it – Pale Blue Dot Jul 23 '21 at 20:58
  • @MarcBaumbach Thanks i had placed `latch.countDown();` outside the submit .Thast why it was not working. – Pale Blue Dot Jul 23 '21 at 21:10

1 Answers1

3

You can use the await method of the CountDownLatch to hold up the thread until the latch reaches zero. You'll want to also modify your submitted task to count down the latch as well. Something like this:

void readMessage(List<Messages> msg) {
    CountDownLatch latch = new CountDownLatch(msg.size); /// lets say msg.size()=5

    for(Messages m : msg) {
        executorService.submit(() -> {
            try {
                dbservice.processInDB(m); //execute saveInDb paaralllely in 5 different threads 
            } finally {
                // One of the messages has been processed, count down the latch by 1
                latch.countDown();
            }
        });
    }

    //Hold the main thread until all 5 threads have completed their work. i.e make latch count to 0 
    try {
        latch.await();
    } catch (InterruptedException e) {
        Thread.currentThread().interrupt();
        throw new RuntimeException(e);
    }
    
    //then send email
    emailService();
}
Marc Baumbach
  • 10,323
  • 2
  • 30
  • 45
  • One thing to note, is that you'll want to be very careful that the line `dbservice.processInDb(m);` never throws. If it does, you'll certainly want to catch all possible exceptions and countdown the latch or somehow propagate that up. Otherwise you'll get stuck with a hanging thread if any of the messages fails. – Marc Baumbach Jul 23 '21 at 21:12
  • .Ok .`dbservice.processInDb(m);` is not throwing any exception .Logic is written in TRY-CATCH.Plus i am using `latch.await();` with `TimeOut` to avoid hanging threads. Thanks for pointing it out – Pale Blue Dot Jul 23 '21 at 21:26
  • 2
    @PaleBlueDot To be more clear, the call to `countDown()` should be done in a `finally` block to ensure it's invoked whether `processInDb` succeeds or fails. For example: `try { dbservice.processInDb(m); } finally { latch.countDown(); }`. – Slaw Jul 23 '21 at 21:32
  • 1
    @PaleBlueDot Note another option, other than `CountDownLatch`, is to make use of [`ExecutorService#invokeAll(Collection)`](https://docs.oracle.com/en/java/javase/16/docs/api/java.base/java/util/concurrent/ExecutorService.html#invokeAll(java.util.Collection)) (or its timeout overload). – Slaw Jul 23 '21 at 21:35
  • 2
    I've put the `countDown()` call into a `finally`... it's really the least we can add to make sure people aren't getting stuck threads. Thanks for the call out @Slaw! – Marc Baumbach Jul 24 '21 at 02:08