2

I have a question based on the ExecutorService Thread flow regulation: I would like to .submit() multiple threads for executon where I would like some threads to wait until specific previous Threads have finished executing. .

So far I know of one such method of doing this by using CountDownLatch(). Following example illustrates 2 threads that need to finish before the third can start:

public class Example{

public static void main(String[] args) throws InterruptedException {

    CountDownLatch cdl = new CountDownLatch(2); //count of 2
    ExecutorService executor = Executors.newCachedThreadPool();
    executor.submit(new Runnable() {
        public void run() {
             method1(); //simulation of a useful method 1
            cdl.countDown(); //reduces the count by one
        }
    });
    executor.submit(new Runnable() {
        public void run() {
             method2(); //simulation of a useful method 2
            cdl.countDown();
        }
    });
    cdl.await(); //will wait until both methods are complete
    executor.submit(new Runnable() { 
        public void run() {
        result(); //simulation of a method that needs previous threads to execute }
        });
    }
}

Quite obvious, such method is not the optimal for such work as, for one thing, one cannot add additional threads after the .await() that do not depend on the CDL itself.

Therefore, what are the better alternatives to regulate Thread flow with ExecutorService that allow better manipulation of Thread execution compared to CDL.

Yoshua Nahar
  • 1,304
  • 11
  • 28
Mora Misina
  • 109
  • 10
  • Thank you for all of your answers! Still do now know whether CDL or Future method is better but at least I now have something to go by. And if any1 knows the answer to this question, please do commment it! – Mora Misina Oct 22 '17 at 10:37
  • Have a look at related SE question: https://stackoverflow.com/questions/3269445/executorservice-how-to-wait-for-all-tasks-to-finish/ – Ravindra babu Oct 23 '17 at 16:28

3 Answers3

3

Just wait for CountDownLatch inside 3rd Runnable

executor.submit(new Runnable() { 
    public void run() {
       cdl.await(); //will wait until both methods are complete
       result(); //simulation of a method that needs previous threads to execute }
    });
}

You might need to declare cdl as final to reference it inside anonymous Runnable instance:

final CountDownLatch cdl = new CountDownLatch(2); //count of 2 

Alternative approach

have a method which will create 3rd task like this:

void createThirdTask() {
    executor.submit(new Runnable() { 
        public void run() {
           result(); //simulation of a method that needs previous threads to execute }
        });
    }
}

Have one shared lock between first two tasks and counter.

private ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
private int count = 2;

Inside method1() and method2() decrement value of CDL and fire third task if it reached zero.

void method1() {
        //your actual code goes here
    try {
        lock.writeLock().lock();
        if(count-- == 0) {
            createThirdTask();
        }
    } finally {
        lock.writeLock().unlock()
    }
}

ReentrantReadWriteLock is there to prevent race conditions.

rkosegi
  • 14,165
  • 5
  • 50
  • 83
  • Thank you for your comment, much appreciated. However, is there any better way of doing simmilar things without using CDL? – Mora Misina Oct 22 '17 at 09:52
  • Willl this method not keep a `Thread` of the executor service in use, thus requiring more threads then necessary? – M. le Rutte Oct 22 '17 at 09:52
  • @MoraMisina : what is problem with using `CountDownLatch` this way? – rkosegi Oct 22 '17 at 09:55
  • @M.leRutte : yes it will consume one worker thread. But I don't see issue here as CachedThreadPoolExecutor will recycle workers from previous finished tasks. – rkosegi Oct 22 '17 at 09:57
  • @rkosegi I have heard that there exist better alternatives for doing the same thing CDL does, such as callback, ..., which do not require creating new objects. Is that correct? – Mora Misina Oct 22 '17 at 09:57
  • @MoraMisina : callback approach is better, but as you need to wait for 2 tasks, thing gets complicated. You will need to wait for both of them and then call your callback. – rkosegi Oct 22 '17 at 10:00
  • @rkosegi: If I understood you correctly = for using 2 or more threads to finish executing you would use CDL but for one you would go for callBacks, correct? Or would you use other alternatives in any (sorry for "basic" questions but I am kinda begginer in Java :)) – Mora Misina Oct 22 '17 at 10:05
3

I would suggest to use CompletableFuture for such kind of thread tasks. It might look like this :

public class RunAfterNThreads {

public static void main(String[] args) throws ExecutionException, InterruptedException {
    CompletableFuture.supplyAsync(RunAfterNThreads::runFirstBatchOfThreads)
            .thenAcceptAsync((t) -> runSecondBatchOfThreads(null)).get();
}

private static Object runSecondBatchOfThreads(Object something) {
    return something;
}

private static <U> U runFirstBatchOfThreads() {
    return null;
}

}

If you want to use some library for this, i would suggest to explore something like Akka (event driven).

mdev
  • 1,366
  • 17
  • 23
  • Thanks for your example! Basically, with the knowledge of CompletableFuture I do not need to separetely learn on the callbacks, as this is the better alternative, right? – Mora Misina Oct 22 '17 at 10:47
  • @MoraMisina i would say just play with this little bit. You will learn difference and then a better way to solve your usecase. – mdev Oct 22 '17 at 18:44
2

I am not sure if Java really has a good standard mechanism in place for you to do that. The internal code for Eclipse has quite some extra classes to handle tasks and their dependencies.

However, I think you might be able to "use" CompletableFuture to do so:

import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class Main {
    public static void main(String[] args) {
        ExecutorService executor = Executors.newCachedThreadPool();
        Runnable r1 = () -> System.out.println("runnable 1");
        Runnable r2 = () -> System.out.println("runnable 2");
        Runnable r3 = () -> System.out.println("runnable 3");


        CompletableFuture<Void> cf1 = CompletableFuture.runAsync(r1, executor);
        CompletableFuture<Void> cf2 = CompletableFuture.runAsync(r2, executor);

        cf1.runAfterBoth(cf2, r3);
  }
}

If your situation is more complex you might be better off searching for a 'Directed Acyclic Graph' task library.

M. le Rutte
  • 3,525
  • 3
  • 18
  • 31
  • Basically, CompletableFuture is an upgraded version of callbacks if I understoop it correctly? – Mora Misina Oct 22 '17 at 10:46
  • I'm not sure what you mean by 'callbacks', as that is a pattern not Java provided class. Maybe [this blog](http://www.baeldung.com/java-completablefuture) might help you learning about `CompletableFuture`. – M. le Rutte Oct 22 '17 at 10:50
  • Just another thing: CompletableFuture is the "optimal" method for waiting a single thread for compleation, correct? – Mora Misina Oct 22 '17 at 10:51