Problem

In the parent thread, several child threads will be forked. It needs to determine whether to terminate the execution of other child threads based on the processing result of some one child thread.

Requirement Assumption

  • After each child thread finishes execution, it can decide to terminate or wait for the execution of other child threads based on its result
  • Each child thread has an “order” attribute, and the execution result of the thread with a smaller “order” value will be prioritized for processing

As depicted above, given that the order attribute of child thread 2 is set to 1, its execution result will be processed with priority. Assuming that the execution result of child thread 2 is to terminate the execution of other child threads rather than waiting, child threads 2 and 3 will be terminated at this point. The main thread will then proceed to execute subsequent logic.

Solution

In the final solution, we’ll utilize the ExecutorService class to manage the thread pool and communicate to the child threads whether termination is necessary via the shutdownNow() method.

Code

import java.util.concurrent.*;
import java.util.*;

public class Main {

    public static void main(String[] args) {
        ExecutorService executor = Executors.newCachedThreadPool();
        List<Future<Result>> futures = new ArrayList<>();

        // put the branches with smaller order values at the beginning
        futures.add(executor.submit(new ChildThread(1)));
        futures.add(executor.submit(new ChildThread(2)));
        futures.add(executor.submit(new ChildThread(3)));

        // iterate over the futures from the lowest order to the highest order
        for (Future<Result> future : futures) {
            try {
                Result result = future.get();
                System.out.println("Processing result from child thread with order " + result.order);
                if (result.terminateOtherBranches) {
                    executor.shutdownNow();
                    break;
                }
            } catch (InterruptedException | ExecutionException e) {
                System.err.println("Error processing result: " + e.getMessage());
            }
        }

        System.out.println("All child threads have finished executing.");
    }

    static class ChildThread implements Callable<Result> {
        private int order;

        public ChildThread(int order) {
            this.order = order;
        }

        @Override
        public Result call() {
            try {
                System.out.println("Child thread with order " + order + " is executing.");
                Thread.sleep(1000 * order); // mock the case that thread with smaller order will finish earlier
                System.out.println("Child thread with order " + order + " has finished.");
                boolean terminateOtherChildThreads = order == 1;
                return new Result(order, terminateOtherChildThreads);
            } catch (InterruptedException e) {
                System.out.println("Child thread with order " + order + " has been terminated");
            }
            return null;
        }
    }

    static class Result {
        private int order;
        private boolean terminateOtherBranches;
        public Result(int order, boolean terminateOtherChildThreads) {
            this.order = order;
            this.terminateOtherBranches = terminateOtherChildThreads;
        }
    }
}

Output

Child thread with order 3 is executing.
Child thread with order 1 is executing.
Child thread with order 2 is executing.
Child thread with order 1 has finished.
Processing result from child thread with order 1
All child threads have finished executing.
Child thread with order 2 has been terminated
Child thread with order 3 has been terminated

Code Underlying Detail

shutdownNow()

Essentially, this method sets the ‘interrupted’ property of the child threads in the thread pool to true using the thread.interrupt() method, as shown in the JDK code.

If a child thread meets certain conditions, it may throw corresponding exceptions, such as InterruptedException or ClosedByInterruptException.

If no conditions are met, by default, only the ‘interrupted’ property of the child thread is set to true. For more details, refer to the JDK documentation.

Solution Summary

  • [Order Property] The main thread sorts each corresponding Future<Result> future for every child thread based on the order property, where the future with the smallest order value is processed first (future.get())
  • [Notifying Child Threads to Terminate] When it’s observed that the execution result of a certain child thread needs terminating other child threads (terminateOtherChildThreads is true), they are notified using shutdownNow
  • [Handling Child Thread Termination] Child threads terminate their execution by catching InterruptedException

Follow up Question

In the “Handling Child Thread Termination” section, it’s evident that the above code handles termination by catching InterruptedException thrown by the blocking method Thread.sleep. However, this isn’t a generic approach. As stated in the JDK documentation, it’s merely an attempt to shut down the thread.

Hence, it’s necessary to address how to ensure that child threads can handle being interrupted promptly when they are set as interrupted.

Share:

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.