Search code examples
javamultithreadingparallel-processingjava.util.concurrent

Join a two different ExecutorService


I wanted to join two threads that are getting executed in ExecutorService.

public class CURD {

  public static ExecutorService executorService = Executors.newCachedThreadPool();
  
 @Autowired
 Logging logging;

  public void Update(List<? extends HBase> save, List<? extends HBase> delete) {
        Thread t = new Thread(() -> {
            System.out.println("Started Main Thread...");
            try {
                Thread.sleep(1500);

            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("End Main Thread...");
        },"Thread-1");

        logging.setPredecessor(t);
        executorService.submit(t);
    }
}

Second Class: This class thread should wait for the first thread to complete. But it doesn't wait for the first thread to complete. I am not sure if this is the right way to do it.

Please can someone let me know how to join two threads that are getting executed in an ExecutorService?

import static com.demo.executorService;

public class Logging {
   
   private Thread predecessor;
   public void  setPredecessor(Thread t) {
        this.predecessor = t;
    }

  private void loggingInfo() {
      Thread run = new Thread( () ->{
                try {
                    if (predecessor != null) {
                        System.out.println(Thread.currentThread().getName() + " Started");
                        predecessor.join();
                        System.out.println(Thread.currentThread().getName() + " Finished");
                    }
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            });
            addTask(run);
   }

   public void addTask(Runnable run) {
        System.out.println("Runnable Thread logAround.....");
        CompletableFuture.runAsync((run), executorService).exceptionally(ex -> {
            System.out.println("exception occurred " + ex);
            return null;
        });
    }
}

Solution

  • If one wants to synchronize among a set of threads one can use the Java CyclicBarrier class:

    A synchronization aid that allows a set of threads to all wait for each other to reach a common barrier point. CyclicBarriers are useful in programs involving a fixed sized party of threads that must occasionally wait for each other. The barrier is called cyclic because it can be re-used after the waiting threads are released.

    To achieve that, first create the CyclicBarrier object with the correspondent number of parties, namely:

    private final CyclicBarrier barrier = new CyclicBarrier(NUMBER_OF_PARIES);
    

    Formally from the Java doc one can read that parties are:

    the number of threads that must invoke {@link #await} before the barrier is tripped

    Informally, parties are the number of threads that will have to call the cyclic barrier and wait, before all of them can move forward.

    Afterward, you need to pass the barrier instance object reference to each of the threads that should wait, and invoke wait (i.e., barrier.await()), accordingly. Something as follows:

      public void Update(..., CyclicBarrier barrier) {
            Thread t = new Thread(() -> {
                System.out.println("Started Main Thread...");
                try {
                     Thread.sleep(1500);
                     barrier.await(); // <--- wait on the barrier
                } catch (InterruptedException | BrokenBarrierException e) {
                     e.printStackTrace();
                 }
                System.out.println("End Main Thread...");
            },"Thread-1");
            ...
        }
    

    Repeat this process to the other threads that must wait. Ensure that the number of parties (i.e., NUMBER_OF_PARIES) matches the number of threads that should wait on the cyclic barrier, otherwise deadlocks will occur.

    Now that you are using the cyclic barrier you can clean up some parts of your code, for instance, you can remove all the logic related to the field predecessor of the Logging class.

    If you just want to make Thread 2 wait for Thread 1, then you can use CountDownLatch, instead.

    A synchronization aid that allows one or more threads to wait until a set of operations being performed in other threads completes. A CountDownLatch is initialized with a given count. The await methods block until the current count reaches zero due to invocations of the countDown() method, after which all waiting threads are released and any subsequent invocations of await return immediately. This is a one-shot phenomenon -- the count cannot be reset. If you need a version that resets the count, consider using a CyclicBarrier.

    First create the CountDownLatch object with only 1 count:

    private final CountDownLatch block_thread2 = new CountDownLatch(1);
    

    and pass it to the Thread 2, and since you want this thread to wait for the Thread 1, call block_thread2.await();

          Thread run = new Thread( () ->{
                       try {
                            ....
                            block_thread2.await(); // wait for Thread 2
                       } catch (InterruptedException e) {
                            // deal with it
                       }
         });
                ...
    

    and to the Thread 1 add wait.countDown();:

      public void Update(...) {
            Thread t = new Thread(() -> {
                       System.out.println("Started Main Thread...");
                       try {
                            Thread.sleep(1500);
                            wait.countDown();
                       } catch (InterruptedException e) {
                            // deal with it
                }
                System.out.println("End Main Thread...");
            },"Thread-1");
            ...
        }
    

    So, in this manner, Thread 2 will wait for Thread 1, but Thread 1 will never wait for Thread 2.