avatarVinotech

Free AI web copilot to create summaries, insights and extended knowledge, download it at here

28771

Abstract

_in">currentThread</span>().<span class="hljs-built_in">getName</span>()); };

    <span class="hljs-comment">// Executing the task using a Thread</span>
    Thread thread = <span class="hljs-keyword">new</span> <span class="hljs-built_in">Thread</span>(task);
    thread.<span class="hljs-built_in">start</span>();
}

}</pre></div><h2 id="154a">When to Use Runnable:</h2><ul><li>When you don’t need a result from the task.</li><li>When you don’t need to throw checked exceptions.</li><li>Suitable for simple background tasks.</li></ul><h2 id="0e17">3. Callable Interface</h2><p id="2bd8"><code>Callable</code> is a similar interface to <code>Runnable</code>, but it’s more powerful. It allows you to return a result and also to throw checked exceptions. A <code>Callable</code> is often used when you need the result of the computation.</p><h2 id="c576">Key Features of Callable:</h2><ul><li><b>Return Value</b>: The <code>call()</code> method returns a value (generic type <code>V</code>). This makes <code>Callable</code> more flexible than <code>Runnable</code>, which cannot return anything.</li><li><b>Can Throw Checked Exceptions</b>: The <code>call()</code> method allows checked exceptions to be thrown. This makes it suitable for tasks where you might need to handle exceptions explicitly (e.g., handling I/O operations).</li><li><b>Used with <code>ExecutorService</code></b>: <code>Callable</code> is typically submitted to an <code>ExecutorService</code>, and its result can be retrieved using a <code>Future</code>.</li></ul><h2 id="1419">Callable Method:</h2><ul><li><code><b>V call()</b></code>: The abstract method that must be implemented. It contains the code to execute and returns a result.</li></ul><p id="f509"><b>Example of <code>Callable</code>:</b></p><div id="c493"><pre><span class="hljs-keyword">import</span> java.util.concurrent.*;

<span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">CallableExample</span> { <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">main</span><span class="hljs-params">(String[] args)</span> <span class="hljs-keyword">throws</span> InterruptedException, ExecutionException { <span class="hljs-type">ExecutorService</span> <span class="hljs-variable">executor</span> <span class="hljs-operator">=</span> Executors.newSingleThreadExecutor();

    <span class="hljs-comment">// Creating a Callable task that returns a result</span>
    Callable&lt;Integer&gt; task = () -&gt; {
        System.out.println(<span class="hljs-string">"Task is being executed"</span>);
        Thread.sleep(<span class="hljs-number">2000</span>); <span class="hljs-comment">// Simulate some work</span>
        <span class="hljs-keyword">return</span> <span class="hljs-number">42</span>;
    };

    <span class="hljs-comment">// Submitting the task to ExecutorService and getting a Future</span>
    Future&lt;Integer&gt; future = executor.submit(task);

    <span class="hljs-comment">// Get the result of the task (this will block until the task is complete)</span>
    <span class="hljs-type">Integer</span> <span class="hljs-variable">result</span> <span class="hljs-operator">=</span> future.get();
    System.out.println(<span class="hljs-string">"Task completed, result: "</span> + result);

    executor.shutdown();
}

}</pre></div><h2 id="285c">When to Use Callable:</h2><ul><li>When you need to return a result from the task.</li><li>When the task might throw checked exceptions (e.g., when dealing with I/O, database, or network operations).</li><li>When using <code>ExecutorService</code> to handle the execution of background tasks and needing a result after task completion.</li></ul><p id="9bdc"><b>Key Differences Between <code>Runnable</code> and <code>Callable</code></b></p><figure id="1e8d"><img src="https://cdn-images-1.readmedium.com/v2/resize:fit:800/1*LirfkNL6klUiwc6mNeNlRg.png"><figcaption></figcaption></figure><h2 id="8319">Usage of Future, Runnable, and Callable Together</h2><ul><li><code>Runnable</code> can be used with an <code>ExecutorService</code> or <code>Thread</code> to execute a task that doesn't return a value.</li><li><code>Callable</code> is typically used with an <code>ExecutorService</code> to execute tasks that return a result or might throw exceptions. When you submit a <code>Callable</code> to an <code>ExecutorService</code>, it returns a <code>Future</code>, which you can use to obtain the result of the <code>Callable</code>.</li></ul><p id="779f"><b>Example: Using <code>Callable</code> with <code>Future</code>:</b></p><div id="b580"><pre>ExecutorService executor = Executors.newFixedThreadPool(<span class="hljs-number">2</span>);

<span class="hljs-comment">// Submit a Callable task that returns a result</span> Future<String> future = executor.submit(() -> { Thread.sleep(<span class="hljs-number">1000</span>); <span class="hljs-keyword">return</span> <span class="hljs-string">"Task Completed"</span>; });

<span class="hljs-comment">// Do other work here while the task is being executed</span>

<span class="hljs-comment">// Block and retrieve the result</span> <span class="hljs-keyword">try</span> { String result = future.<span class="hljs-keyword">get</span>(); System.<span class="hljs-keyword">out</span>.println(<span class="hljs-string">"Task result: "</span> + result); } <span class="hljs-keyword">catch</span> (InterruptedException | ExecutionException e) { e.printStackTrace(); }

executor.shutdown();</pre></div><ul><li><b>Runnable</b> is for tasks that don’t need to return a result.</li><li><b>Callable</b> is for tasks that need to return a result and may throw exceptions.</li><li><b>Future</b> is used to represent the result of an asynchronous task, allowing you to check the task’s status, retrieve its result, or cancel it if necessary.</li></ul><h1 id="f796">Examples</h1><h2 id="edc9">1. ExecutorService using submit(Runnable task)</h2><p id="40bd">Here’s an example of using the <code>ExecutorService</code> with <code>submit(Runnable task)</code>. In this example, we will create a thread pool with multiple threads using <code>ExecutorService</code>, submit several <code>Runnable</code> tasks for execution, and observe the output.</p><h2 id="295e">Use Case Scenario:</h2><p id="4639">Suppose we are processing multiple orders concurrently in an e-commerce system, and each task represents the processing of a single order. Since we don’t need a return value, we’ll use <code>Runnable</code>.</p><div id="ba94"><pre><span class="hljs-keyword">import</span> java.util.concurrent.ExecutorService; <span class="hljs-keyword">import</span> java.util.concurrent.Executors;

<span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">OrderProcessingExample</span> {

<span class="hljs-comment">// Simulate order processing</span>
<span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">ProcessOrderTask</span> <span class="hljs-keyword">implements</span> <span class="hljs-title class_">Runnable</span> {
    <span class="hljs-keyword">private</span> <span class="hljs-keyword">final</span> <span class="hljs-type">int</span> orderId;

    <span class="hljs-keyword">public</span> <span class="hljs-title function_">ProcessOrderTask</span><span class="hljs-params">(<span class="hljs-type">int</span> orderId)</span> {
        <span class="hljs-built_in">this</span>.orderId = orderId;
    }

    <span class="hljs-meta">@Override</span>
    <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">run</span><span class="hljs-params">()</span> {
        System.out.println(<span class="hljs-string">"Processing order: "</span> + orderId + <span class="hljs-string">" by thread: "</span> + Thread.currentThread().getName());
        <span class="hljs-comment">// Simulate some processing time</span>
        <span class="hljs-keyword">try</span> {
            Thread.sleep(<span class="hljs-number">1000</span>);  <span class="hljs-comment">// Simulate time to process an order</span>
        } <span class="hljs-keyword">catch</span> (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(<span class="hljs-string">"Completed processing order: "</span> + orderId);
    }
}

<span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">main</span><span class="hljs-params">(String[] args)</span> {
    <span class="hljs-comment">// Create an ExecutorService with a fixed thread pool of 3 threads</span>
    <span class="hljs-type">ExecutorService</span> <span class="hljs-variable">executorService</span> <span class="hljs-operator">=</span> Executors.newFixedThreadPool(<span class="hljs-number">3</span>);

    <span class="hljs-comment">// Submit 5 order processing tasks to the executor</span>
    <span class="hljs-keyword">for</span> (<span class="hljs-type">int</span> <span class="hljs-variable">i</span> <span class="hljs-operator">=</span> <span class="hljs-number">1</span>; i &lt;= <span class="hljs-number">5</span>; i++) {
        executorService.submit(<span class="hljs-keyword">new</span> <span class="hljs-title class_">ProcessOrderTask</span>(i));
    }

    <span class="hljs-comment">// Shutdown the executor service after tasks are submitted</span>
    executorService.shutdown();
}

}</pre></div><p id="dabe"><b>Output:</b></p><div id="7ff9"><pre>Processing <span class="hljs-keyword">order</span>: <span class="hljs-number">1</span> <span class="hljs-keyword">by</span> thread: pool-<span class="hljs-number">1</span>-thread-<span class="hljs-number">1</span> Processing <span class="hljs-keyword">order</span>: <span class="hljs-number">2</span> <span class="hljs-keyword">by</span> thread: pool-<span class="hljs-number">1</span>-thread-<span class="hljs-number">2</span> Processing <span class="hljs-keyword">order</span>: <span class="hljs-number">3</span> <span class="hljs-keyword">by</span> thread: pool-<span class="hljs-number">1</span>-thread-<span class="hljs-number">3</span> Completed processing <span class="hljs-keyword">order</span>: <span class="hljs-number">1</span> Processing <span class="hljs-keyword">order</span>: <span class="hljs-number">4</span> <span class="hljs-keyword">by</span> thread: pool-<span class="hljs-number">1</span>-thread-<span class="hljs-number">1</span> Completed processing <span class="hljs-keyword">order</span>: <span class="hljs-number">2</span> Processing <span class="hljs-keyword">order</span>: <span class="hljs-number">5</span> <span class="hljs-keyword">by</span> thread: pool-<span class="hljs-number">1</span>-thread-<span class="hljs-number">2</span> Completed processing <span class="hljs-keyword">order</span>: <span class="hljs-number">3</span> Completed processing <span class="hljs-keyword">order</span>: <span class="hljs-number">4</span> Completed processing <span class="hljs-keyword">order</span>: <span class="hljs-number">5</span></pre></div><h2 id="8d8e">Explanation:</h2><ol><li>We create a <b>fixed thread pool</b> of 3 threads using <code>Executors.newFixedThreadPool(3)</code>.</li><li>We create and submit 5 <code>Runnable</code> tasks (<code>ProcessOrderTask</code>) to the <code>ExecutorService</code>. Each task simulates the processing of an order, represented by an order ID.</li><li>Each task runs on a separate thread from the thread pool. Since we have a pool of 3 threads, the first 3 tasks will be processed simultaneously. The remaining tasks wait until a thread becomes available.</li><li>After submitting all the tasks, we call <code>shutdown()</code> to prevent the <code>ExecutorService</code> from accepting new tasks and ensure that it shuts down after completing the currently submitted tasks.</li></ol><h2 id="9c5d">2. ExecutorService using submit(Callable<T> task)</h2><p id="317f">Here’s an example of using the <code>ExecutorService</code> with <code>submit(Callable<T> task)</code>. In this example, we will create a thread pool, submit several <code>Callable</code> tasks, and get their results using <code>Future</code>.</p><h2 id="ea3e">Use Case Scenario:</h2><p id="87e2">Suppose we are calculating the prices of items in an e-commerce system, where each task represents fetching the price of a single item. Since we need to return a result (the item price), we’ll use <code>Callable</code>.</p><div id="7509"><pre><span class="hljs-keyword">import</span> java.util.concurrent.Callable; <span class="hljs-keyword">import</span> java.util.concurrent.ExecutorService; <span class="hljs-keyword">import</span> java.util.concurrent.Executors; <span class="hljs-keyword">import</span> java.util.concurrent.Future; <span class="hljs-keyword">import</span> java.util.List; <span class="hljs-keyword">import</span> java.util.ArrayList;

<span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">PriceCalculationExample</span> {

<span class="hljs-comment">// Simulate price calculation task for an item</span>
<span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">PriceTask</span> <span class="hljs-keyword">implements</span> <span class="hljs-title class_">Callable</span>&lt;Double&gt; {
    <span class="hljs-keyword">private</span> <span class="hljs-keyword">final</span> <span class="hljs-type">int</span> itemId;

    <span class="hljs-keyword">public</span> <span class="hljs-title function_">PriceTask</span><span class="hljs-params">(<span class="hljs-type">int</span> itemId)</span> {
        <span class="hljs-built_in">this</span>.itemId = itemId;
    }

    <span class="hljs-meta">@Override</span>
    <span class="hljs-keyword">public</span> Double <span class="hljs-title function_">call</span><span class="hljs-params">()</span> <span class="hljs-keyword">throws</span> Exception {
        System.out.println(<span class="hljs-string">"Fetching price for item: "</span> + itemId + <span class="hljs-string">" by thread: "</span> + Thread.currentThread().getName());
        <span class="hljs-comment">// Simulate some processing time</span>
        Thread.sleep(<span class="hljs-number">1000</span>);  <span class="hljs-comment">// Simulate time to calculate price</span>
        <span class="hljs-type">double</span> <span class="hljs-variable">price</span> <span class="hljs-operator">=</span> Math.random() * <span class="hljs-number">100</span>; <span class="hljs-comment">// Simulate price between 0 and 100</span>
        System.out.println(<span class="hljs-string">"Price for item "</span> + itemId + <span class="hljs-string">" is: $"</span> + price);
        <span class="hljs-keyword">return</span> price;
    }
}

<span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">main</span><span class="hljs-params">(String[] args)</span> <span class="hljs-keyword">throws</span> Exception {
    <span class="hljs-comment">// Create an ExecutorService with a fixed thread pool of 3 threads</span>
    <span class="hljs-type">ExecutorService</span> <span class="hljs-variable">executorService</span> <span class="hljs-operator">=</span> Executors.newFixedThreadPool(<span class="hljs-number">3</span>);

    <span class="hljs-comment">// Create a list to hold Future objects that store the results</span>
    List&lt;Future&lt;Double&gt;&gt; futures = <span class="hljs-keyword">new</span> <span class="hljs-title class_">ArrayList</span>&lt;&gt;();

    <span class="hljs-comment">// Submit 5 price calculation tasks to the executor</span>
    <span class="hljs-keyword">for</span> (<span class="hljs-type">int</span> <span class="hljs-variable">i</span> <span class="hljs-operator">=</span> <span class="hljs-number">1</span>; i &lt;= <span class="hljs-number">5</span>; i++) {
        Future&lt;Double&gt; future = executorService.submit(<span class="hljs-keyword">new</span> <span class="hljs-title class_">PriceTask</span>(i));
        futures.add(future);  <span class="hljs-comment">// Store the future result</span>
    }

    <span class="hljs-comment">// Retrieve and display the results of each task</span>
    <span class="hljs-keyword">for</span> (<span class="hljs-type">int</span> <span class="hljs-variable">i</span> <span class="hljs-operator">=</span> <span class="hljs-number">0</span>; i &lt; futures.size(); i++) {
        <span class="hljs-type">Double</span> <span class="hljs-variable">price</span> <span class="hljs-operator">=</span> futures.get(i).get();  <span class="hljs-comment">// This will block until the result is available</span>
        System.out.println(<span class="hljs-string">"Final price for item "</span> + (i + <span class="hljs-number">1</span>) + <span class="hljs-string">" is: $"</span> + price);
    }

    <span class="hljs-comment">// Shutdown the executor service after tasks are submitted</span>
    executorService.shutdown();
}

}</pre></div><p id="6e91"><b>Output:</b></p><div id="9a50"><pre><span class="hljs-type">Fetching</span> price <span class="hljs-keyword">for</span> item: <span class="hljs-number">1</span> by thread: pool<span class="hljs-operator">-</span><span class="hljs-number">1</span><span class="hljs-operator">-</span>thread<span class="hljs-operator">-</span><span class="hljs-number">1</span> <span class="hljs-type">Fetching</span> price <span class="hljs-keyword">for</span> item: <span class="hljs-number">2</span> by thread: pool<span class="hljs-operator">-</span><span class="hljs-number">1</span><span class="hljs-operator">-</span>thread<span class="hljs-operator">-</span><span class="hljs-number">2</span> <span class="hljs-type">Fetching</span> price <span class="hljs-keyword">for</span> item: <span class="hljs-number">3</span> by thread: pool<span class="hljs-operator">-</span><span class="hljs-number">1</span><span class="hljs-operator">-</span>thread<span class="hljs-operator">-</span><span class="hljs-number">3</span> <span class="hljs-type">Price</span> <span class="hljs-keyword">for</span> item <span class="hljs-number">1</span> <span class="hljs-keyword">is</span>: <span class="hljs-variable">32</span>.<span class="hljs-number">3548353899836</span> <span class="hljs-type">Fetching</span> price <span class="hljs-keyword">for</span> item: <span class="hljs-number">4</span> by thread: pool<span class="hljs-operator">-</span><span class="hljs-number">1</span><span class="hljs-operator">-</span>thread<span class="hljs-operator">-</span><span class="hljs-number">1</span> <span class="hljs-type">Price</span> <span class="hljs-keyword">for</span> item <span class="hljs-number">2</span> <span class="hljs-keyword">is</span>: <span class="hljs-variable">25</span>.<span class="hljs-number">243423111847564</span> <span class="hljs-type">Fetching</span> price <span class="hljs-keyword">for</span> item: <span class="hljs-number">5</span> by thread: pool<span class="hljs-operator">-</span><span class="hljs-number">1</span><span class="hljs-operator">-</span>thread<span class="hljs-operator">-</span><span class="hljs-number">2</span> <span class="hljs-type">Price</span> <span class="hljs-keyword">for</span> item <span class="hljs-number">3</span> <span class="hljs-keyword">is</span>: <span class="hljs-variable">57</span>.<span class="hljs-number">83639876554309</span> <span class="hljs-type">Price</span> <span class="hljs-keyword">for</span> item <span class="hljs-number">4</span> <span class="hljs-keyword">is</span>: <span class="hljs-variable">41</span>.<span class="hljs-number">63741232512334</span> <span class="hljs-type">Price</span> <span class="hljs-keyword">for</span> item <span class="hljs-number">5</span> <span class="hljs-keyword">is</span>: <span class="hljs-variable">11</span>.<span class="hljs-number">482103439847643</span> <span class="hljs-type">Final</span> price <span class="hljs-keyword">for</span> item <span class="hljs-number">1</span> <span class="hljs-keyword">is</span>: <span class="hljs-variable">32</span>.<span class="hljs-number">3548353899836</span> <span class="hljs-type">Final</span> price <span class="hljs-keyword">for</span> item <span class="hljs-number">2</span> <span class="hljs-keyword">is</span>: <span class="hljs-variable">25</span>.<span class="hljs-number">243423111847564</span> <span class="hljs-type">Final</span> price <span class="hljs-keyword">for</span> item <span class="hljs-number">3</span> <span class="hljs-keyword">is</span>: <span class="hljs-variable">57</span>.<span class="hljs-number">83639876554309</span> <span class="hljs-type">Final</span> price <span class="hljs-keyword">for</span> item <span class="hljs-number">4</span> <span class="hljs-keyword">is</span>: <span class="hljs-variable">41</span>.<span class="hljs-number">63741232512334</span> <span class="hljs-type">Final</span> price <span class="hljs-keyword">for</span> item <span class="hljs-number">5</span> <span class="hljs-keyword">is</span>: <span class="hljs-variable">11</span>.<span class="hljs-number">482103439847643</span></pre></div><h2 id="d893">Explanation:</h2><ol><li><b>Thread Pool Creation</b>: We create a fixed thread pool with 3 threads using <code>Executors.newFixedThreadPool(3)</code>.</li><li><b>Callable Task (PriceTask)</b>: We define a <code>Callable</code> task (<code>PriceTask</code>) that simulates fetching the price of an item by waiting for 1 second and then generating a random price between 0 and 100.</li><li><b>Submit Callable Tasks</b>: We submit 5 <code>PriceTask</code> objects to the <code>ExecutorService</code>. Each task will calculate the price for one item, and the result will be stored in a <code>Future</code> object.</li><li><b>Future Handling</b>: We store the <code>Future</code> objects in a list and then call <code>get()</code> on each future to retrieve the calculated price. The <code>get()</code> method blocks until the price is available, ensuring we get the correct result.</li><li><b>Shutdown</b>: After submitting all the tasks, we call <code>shutdown()</code> on the <code>ExecutorService</code> to ensure no new tasks are accepted and the service shuts down after completing the submitted tasks.</li></ol><h2 id="451e">3. ExecutorService using invokeAll(Callable<T> task)</h2><p id="2db4">Here’s an example of using <code>ExecutorService</code> with the <code>invokeAll(Collection<? extends Callable<T>> tasks)</code> method in Java. This method executes a collection of <code>Callable</code> tasks and returns a list of <code>Future</code> objects representing the results of the tasks. The <code>invokeAll</code> method blocks until all tasks have completed.</p><h2 id="343f">Use Case Scenario:</h2><p id="42db">Let’s imagine we are fetching data from multiple remote APIs simultaneously. Each API call is simulated as a separate <code>Callable</code> task. We want to execute all API calls concurrently and retrieve the results once all tasks are complete.</p><div id="d501"><pre><span class="hljs-keyword">import</span> java.util.concurrent.*; <span class="hljs-keyword">import</span> java.util.List; <span class="hljs-keyword">import</span> java.util.ArrayList;

<span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">APIDataFetchExample</span> {

<span class="hljs-comment">// Simulate API data fetching task</span>
<span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">FetchDataTask</span> <span class="hljs-keyword">implements</span> <span class="hljs-title class_">Callable</span>&lt;String&gt; {
    <span class="hljs-keyword">private</span> <span class="hljs-keyword">final</span> String apiName;

    <span class="hljs-keyword">public</span> <span class="hljs-title function_">FetchDataTask</span><span class="hljs-params">(String apiName)</span> {
        <span class="hljs-built_in">this</span>.apiName = apiName;
    }

    <span class="hljs-meta">@Override</span>
    <span class="hljs-keyword">public</span> String <span class="hljs-title function_">call</span><span class="hljs-params">()</span> <span class="hljs-keyword">throws</span> Exception {
        System.out.println(<span class="hljs-string">"Fetching data from: "</span> + apiName + <span class="hljs-string">" by thread: "</span> + Thread.currentThread().getName());
        <span class="hljs-comment">// Simulate some delay in fetching data</span>
        Thread.sleep((<span class="hljs-type">long</span>) (Math.random() * <span class="hljs-number">2000</span>));  <span class="hljs-comment">// Simulate variable time for each API call</span>
        <span class="hljs-type">String</span> <span class="hljs-variable">result</span> <span class="hljs-operator">=</span> apiName + <span class="hljs-string">" data"</span>;
        System.out.println(<span class="hljs-string">"Data fetched from "</span> + apiName);
        <span class="hljs-keyword">return</span> result;
    }
}

<span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">main</span><span class="hljs-params">(String[] args)</span> <span class="hljs-keyword">throws</span> InterruptedException, ExecutionException {
    <span class="hljs-comment">// Create an ExecutorService with a fixed thread pool of 3 threads</span>
    <span class="hljs-type">ExecutorService</span> <span class="hljs-variable">executorService</span> <span class="hljs-operator">=</span> Executors.newFixedThreadPool(<span class="hljs-number">3</span>);

    <span class="hljs-comment">// Create a list of Callable tasks to fetch data from multiple APIs</span>
    List&lt;Callable&lt;String&gt;&gt; apiTasks = <span class="hljs-keyword">new</span> <span class="hljs-title class_">ArrayList</span>&lt;&gt;();
    apiTasks.add(<span class="hljs-keyword">new</span> <span class="hljs-title class_">FetchDataTask</span>(<span class="hljs-string">"API_1"</span>));
    apiTasks.add(<span class="hljs-keyword">new</span> <span class="hljs-title class_">FetchDataTask</span>(<span class="hljs-string">"API_2"</span>));
    apiTasks.add(<span class="hljs-keyword">new</span> <span class="hljs-title class_">FetchDataTask</span>(<span class="hljs-string">"API_3"</span>));
    apiTasks.add(<span class="hljs-keyword">new</span> <span class="hljs-title class_">FetchDataTask</span>(<span class="hljs-string">"API_4"</span>));
    apiTasks.add(<span class="hljs-keyword">new</span> <span class="hljs-title class_">FetchDataTask</span>(<span class="hljs-string">"API_5"</span>));

    <span class="hljs-comment">// Execute all tasks and wait for them to complete using invokeAll</span>
    List&lt;Future&lt;String&gt;&gt; results = executorService.invokeAll(apiTasks);

    <span class="hljs-comment">// Process the results</span>
    <span class="hljs-keyword">for</span> (<span class="hljs-type">int</span> <span class="hljs-variable">i</span> <span class="hljs-operator">=</span> <span class="hljs-number">0</span>; i &lt; results.size(); i++) {
        <span class="hljs-type">String</span> <span class="hljs-variable">result</span> <span class="hljs-operator">=</span> results.get(i).get();  <span class="hljs-comment">// This will block until the task is done</span>
        System.out.println(<span class="hljs-string">"Result of API task "</span> + (i + <span class="hljs-number">1</span>) + <span class="hljs-string">": "</span> + result);
    }

    <span class="hljs-comment">// Shutdown the executor service</span>
    executorService.shutdown();
}

}</pre></div><p id="81dd"><b>Output:</b></p><div id="ea80"><pre>Fetching data <span class="hljs-keyword">from</span>: API_1 <span class="hljs-keyword">by</span> thread: pool<span class="hljs-number">-1</span>-thread<span class="hljs-numbe

Options

r">-1</span> Fetching data <span class="hljs-keyword">from</span>: API_2 <span class="hljs-keyword">by</span> thread: pool<span class="hljs-number">-1</span>-thread<span class="hljs-number">-2</span> Fetching data <span class="hljs-keyword">from</span>: API_3 <span class="hljs-keyword">by</span> thread: pool<span class="hljs-number">-1</span>-thread<span class="hljs-number">-3</span> Data fetched <span class="hljs-keyword">from</span> API_2 Fetching data <span class="hljs-keyword">from</span>: API_4 <span class="hljs-keyword">by</span> thread: pool<span class="hljs-number">-1</span>-thread<span class="hljs-number">-2</span> Data fetched <span class="hljs-keyword">from</span> API_3 Fetching data <span class="hljs-keyword">from</span>: API_5 <span class="hljs-keyword">by</span> thread: pool<span class="hljs-number">-1</span>-thread<span class="hljs-number">-3</span> Data fetched <span class="hljs-keyword">from</span> API_1 Data fetched <span class="hljs-keyword">from</span> API_5 Data fetched <span class="hljs-keyword">from</span> API_4 Result of API task <span class="hljs-number">1</span>: API_1 data Result of API task <span class="hljs-number">2</span>: API_2 data Result of API task <span class="hljs-number">3</span>: API_3 data Result of API task <span class="hljs-number">4</span>: API_4 data Result of API task <span class="hljs-number">5</span>: API_5 data</pre></div><h2 id="eeae">Explanation:</h2><ol><li><b>Thread Pool Creation</b>: We create a fixed thread pool with 3 threads using <code>Executors.newFixedThreadPool(3)</code>.</li><li><b>Callable Task (FetchDataTask)</b>: Each task simulates fetching data from an API. The <code>call()</code> method simulates a delay (random between 0 and 2000 milliseconds) to mimic different response times for each API.</li><li><b>invokeAll</b>: We use <code>invokeAll()</code> to submit all the tasks at once. This method blocks until all tasks are completed, meaning all data fetching operations will be executed concurrently by the thread pool.</li><li><b>Result Processing</b>: After all tasks are completed, we use <code>Future.get()</code> on each <code>Future</code> object in the result list to retrieve the data fetched by each API.</li><li><b>Shutdown</b>: Once all tasks are processed, we shut down the <code>ExecutorService</code>.</li></ol><h2 id="e45a">4. ExecutorService using invokeAny(Collection<? extends Callable<T>> tasks)</h2><p id="21a5">Here’s an example of using <code>ExecutorService</code> with the <code>invokeAny(Collection<? extends Callable<T>> tasks)</code> method in Java. This method executes a collection of <code>Callable</code> tasks and returns the result of the first successfully completed task. If any task throws an exception, it will be propagated, and execution will stop.</p><h2 id="be5e">Use Case Scenario:</h2><p id="5132">Let’s imagine we are trying to find the price of a product from multiple sources (APIs). We want to retrieve the price from the fastest source available. Each source is represented by a <code>Callable</code> task that simulates fetching the price.</p><div id="a3b1"><pre><span class="hljs-keyword">import</span> java.util.concurrent.*; <span class="hljs-keyword">import</span> java.util.ArrayList; <span class="hljs-keyword">import</span> java.util.List;

<span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">FastestPriceFetcherExample</span> {

<span class="hljs-comment">// Simulate price fetching task from different sources</span>
<span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">PriceFetchTask</span> <span class="hljs-keyword">implements</span> <span class="hljs-title class_">Callable</span>&lt;Double&gt; {
    <span class="hljs-keyword">private</span> <span class="hljs-keyword">final</span> String sourceName;
    <span class="hljs-keyword">private</span> <span class="hljs-keyword">final</span> <span class="hljs-type">int</span> delay; <span class="hljs-comment">// Delay to simulate fetch time</span>

    <span class="hljs-keyword">public</span> <span class="hljs-title function_">PriceFetchTask</span><span class="hljs-params">(String sourceName, <span class="hljs-type">int</span> delay)</span> {
        <span class="hljs-built_in">this</span>.sourceName = sourceName;
        <span class="hljs-built_in">this</span>.delay = delay;
    }

    <span class="hljs-meta">@Override</span>
    <span class="hljs-keyword">public</span> Double <span class="hljs-title function_">call</span><span class="hljs-params">()</span> <span class="hljs-keyword">throws</span> Exception {
        System.out.println(<span class="hljs-string">"Fetching price from: "</span> + sourceName + <span class="hljs-string">" (delay: "</span> + delay + <span class="hljs-string">"ms)"</span>);
        <span class="hljs-comment">// Simulate variable fetch time</span>
        Thread.sleep(delay);
        <span class="hljs-type">double</span> <span class="hljs-variable">price</span> <span class="hljs-operator">=</span> Math.random() * <span class="hljs-number">100</span>; <span class="hljs-comment">// Simulate price between 0 and 100</span>
        System.out.println(<span class="hljs-string">"Price fetched from "</span> + sourceName + <span class="hljs-string">": $"</span> + price);
        <span class="hljs-keyword">return</span> price;
    }
}

<span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">main</span><span class="hljs-params">(String[] args)</span> {
    <span class="hljs-comment">// Create an ExecutorService with a fixed thread pool of 3 threads</span>
    <span class="hljs-type">ExecutorService</span> <span class="hljs-variable">executorService</span> <span class="hljs-operator">=</span> Executors.newFixedThreadPool(<span class="hljs-number">3</span>);

    <span class="hljs-comment">// Create a list of Callable tasks with different delays</span>
    List&lt;Callable&lt;Double&gt;&gt; priceFetchTasks = <span class="hljs-keyword">new</span> <span class="hljs-title class_">ArrayList</span>&lt;&gt;();
    priceFetchTasks.add(<span class="hljs-keyword">new</span> <span class="hljs-title class_">PriceFetchTask</span>(<span class="hljs-string">"Source 1"</span>, <span class="hljs-number">2000</span>)); <span class="hljs-comment">// 2 seconds delay</span>
    priceFetchTasks.add(<span class="hljs-keyword">new</span> <span class="hljs-title class_">PriceFetchTask</span>(<span class="hljs-string">"Source 2"</span>, <span class="hljs-number">1000</span>)); <span class="hljs-comment">// 1 second delay</span>
    priceFetchTasks.add(<span class="hljs-keyword">new</span> <span class="hljs-title class_">PriceFetchTask</span>(<span class="hljs-string">"Source 3"</span>, <span class="hljs-number">1500</span>)); <span class="hljs-comment">// 1.5 seconds delay</span>
    priceFetchTasks.add(<span class="hljs-keyword">new</span> <span class="hljs-title class_">PriceFetchTask</span>(<span class="hljs-string">"Source 4"</span>, <span class="hljs-number">3000</span>)); <span class="hljs-comment">// 3 seconds delay</span>

    <span class="hljs-keyword">try</span> {
        <span class="hljs-comment">// Invoke any task and get the result of the first one that completes</span>
        <span class="hljs-type">Double</span> <span class="hljs-variable">fastestPrice</span> <span class="hljs-operator">=</span> executorService.invokeAny(priceFetchTasks);
        System.out.println(<span class="hljs-string">"Fastest fetched price: $"</span> + fastestPrice);
    } <span class="hljs-keyword">catch</span> (InterruptedException | ExecutionException e) {
        e.printStackTrace();
    } <span class="hljs-keyword">finally</span> {
        <span class="hljs-comment">// Shutdown the executor service</span>
        executorService.shutdown();
    }
}

}</pre></div><p id="74fe"><b>Output</b></p><div id="1054"><pre><span class="hljs-attr">Fetching price from:</span> <span class="hljs-string">Source</span> <span class="hljs-number">1</span> <span class="hljs-string">(delay:</span> <span class="hljs-string">2000ms)</span> <span class="hljs-attr">Fetching price from:</span> <span class="hljs-string">Source</span> <span class="hljs-number">2</span> <span class="hljs-string">(delay:</span> <span class="hljs-string">1000ms)</span> <span class="hljs-attr">Fetching price from:</span> <span class="hljs-string">Source</span> <span class="hljs-number">3</span> <span class="hljs-string">(delay:</span> <span class="hljs-string">1500ms)</span> <span class="hljs-attr">Price fetched from Source 2:</span> <span class="hljs-string">45.25639845363515</span> <span class="hljs-attr">Fastest fetched price:</span> <span class="hljs-string">45.25639845363515</span> <span class="hljs-attr">Price fetched from Source 1:</span> <span class="hljs-string">73.48567579210142</span> <span class="hljs-attr">Price fetched from Source 3:</span> <span class="hljs-string">34.83928121117716</span> <span class="hljs-attr">Price fetched from Source 4:</span> <span class="hljs-string">$29.00117935605395</span></pre></div><h2 id="0e5b">Explanation:</h2><ol><li><b>Thread Pool Creation</b>: We create a fixed thread pool with 3 threads using <code>Executors.newFixedThreadPool(3)</code>.</li><li><b>Callable Task (PriceFetchTask)</b>: Each task simulates fetching a price from a source. The <code>call()</code> method includes a delay (simulating the time taken to fetch the price) and returns a randomly generated price.</li><li><b>invokeAny</b>: We use <code>invokeAny()</code> to submit all tasks at once. This method blocks until the first task completes successfully, returning the result of that task.</li><li><b>Output</b>: The output shows which task was executed first, along with the corresponding price fetched from that source. If multiple tasks complete, only the result of the first one that finished successfully is returned.</li><li><b>Shutdown</b>: After obtaining the result, we shut down the <code>ExecutorService</code>.</li></ol><h2 id="ee05">5. ExecutorService using Shutdown and Termination Methods</h2><p id="657d">Here’s an example of using the shutdown and termination methods of <code>ExecutorService</code> in Java. This example will demonstrate how to gracefully shut down an executor service and ensure that all tasks have completed before the program exits.</p><h2 id="dc29">Use Case Scenario:</h2><p id="bc4e">Let’s consider a scenario where we are processing a list of tasks that simulate downloading files. We want to ensure that all downloads are completed before the application shuts down.</p><div id="03d1"><pre><span class="hljs-keyword">import</span> java.util.concurrent.*;

<span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">FileDownloadExample</span> {

<span class="hljs-comment">// Simulate a file download task</span>
<span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">DownloadTask</span> <span class="hljs-keyword">implements</span> <span class="hljs-title class_">Runnable</span> {
    <span class="hljs-keyword">private</span> <span class="hljs-keyword">final</span> String fileName;

    <span class="hljs-keyword">public</span> <span class="hljs-title function_">DownloadTask</span><span class="hljs-params">(String fileName)</span> {
        <span class="hljs-built_in">this</span>.fileName = fileName;
    }

    <span class="hljs-meta">@Override</span>
    <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">run</span><span class="hljs-params">()</span> {
        System.out.println(<span class="hljs-string">"Starting download: "</span> + fileName + <span class="hljs-string">" by thread: "</span> + Thread.currentThread().getName());
        <span class="hljs-comment">// Simulate time taken to download a file</span>
        <span class="hljs-keyword">try</span> {
            Thread.sleep((<span class="hljs-type">long</span>) (Math.random() * <span class="hljs-number">3000</span>)); <span class="hljs-comment">// Random sleep between 0 and 3 seconds</span>
        } <span class="hljs-keyword">catch</span> (InterruptedException e) {
            Thread.currentThread().interrupt(); <span class="hljs-comment">// Restore interrupted status</span>
        }
        System.out.println(<span class="hljs-string">"Completed download: "</span> + fileName);
    }
}

<span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">main</span><span class="hljs-params">(String[] args)</span> {
    <span class="hljs-comment">// Create an ExecutorService with a fixed thread pool of 2 threads</span>
    <span class="hljs-type">ExecutorService</span> <span class="hljs-variable">executorService</span> <span class="hljs-operator">=</span> Executors.newFixedThreadPool(<span class="hljs-number">2</span>);

    <span class="hljs-comment">// Submit download tasks to the executor</span>
    <span class="hljs-keyword">for</span> (<span class="hljs-type">int</span> <span class="hljs-variable">i</span> <span class="hljs-operator">=</span> <span class="hljs-number">1</span>; i &lt;= <span class="hljs-number">5</span>; i++) {
        executorService.submit(<span class="hljs-keyword">new</span> <span class="hljs-title class_">DownloadTask</span>(<span class="hljs-string">"File_"</span> + i));
    }

    <span class="hljs-comment">// Initiate shutdown of the executor service</span>
    executorService.shutdown();
    System.out.println(<span class="hljs-string">"Shutdown initiated. No new tasks will be accepted."</span>);

    <span class="hljs-keyword">try</span> {
        <span class="hljs-comment">// Wait for existing tasks to terminate, with a timeout</span>
        <span class="hljs-keyword">if</span> (!executorService.awaitTermination(<span class="hljs-number">5</span>, TimeUnit.SECONDS)) {
            System.out.println(<span class="hljs-string">"Some tasks are still running after timeout. Forcing shutdown..."</span>);
            executorService.shutdownNow(); <span class="hljs-comment">// Force shutdown if tasks are still running</span>
        }
    } <span class="hljs-keyword">catch</span> (InterruptedException e) {
        System.err.println(<span class="hljs-string">"Shutdown interrupted!"</span>);
        executorService.shutdownNow(); <span class="hljs-comment">// Force shutdown on interruption</span>
    }

    System.out.println(<span class="hljs-string">"Executor service has been shut down."</span>);
}

}</pre></div><p id="efa8"><b>Output</b></p><div id="cc9b"><pre><span class="hljs-attr">Starting download: File_1 by thread:</span> <span class="hljs-string">pool-1-thread-1</span> <span class="hljs-attr">Starting download: File_2 by thread:</span> <span class="hljs-string">pool-1-thread-2</span> <span class="hljs-attr">Starting download: File_3 by thread:</span> <span class="hljs-string">pool-1-thread-1</span> <span class="hljs-attr">Completed download:</span> <span class="hljs-string">File_2</span> <span class="hljs-attr">Completed download:</span> <span class="hljs-string">File_1</span> <span class="hljs-attr">Starting download: File_4 by thread:</span> <span class="hljs-string">pool-1-thread-2</span> <span class="hljs-attr">Completed download:</span> <span class="hljs-string">File_3</span> <span class="hljs-attr">Completed download:</span> <span class="hljs-string">File_4</span> <span class="hljs-attr">Starting download: File_5 by thread:</span> <span class="hljs-string">pool-1-thread-1</span> <span class="hljs-attr">Completed download:</span> <span class="hljs-string">File_5</span> <span class="hljs-string">Shutdown</span> <span class="hljs-string">initiated.</span> <span class="hljs-literal">No</span> <span class="hljs-string">new</span> <span class="hljs-string">tasks</span> <span class="hljs-string">will</span> <span class="hljs-string">be</span> <span class="hljs-string">accepted.</span> <span class="hljs-string">Executor</span> <span class="hljs-string">service</span> <span class="hljs-string">has</span> <span class="hljs-string">been</span> <span class="hljs-string">shut</span> <span class="hljs-string">down.</span></pre></div><h2 id="0db9">Explanation:</h2><ol><li><b>Thread Pool Creation</b>: We create a fixed thread pool with 2 threads using <code>Executors.newFixedThreadPool(2)</code>.</li><li><b>Runnable Task (DownloadTask)</b>: Each task simulates downloading a file. The <code>run()</code> method includes a delay to simulate the time taken to download.</li><li><b>Submitting Tasks</b>: We submit 5 download tasks to the executor service.</li><li><b>Shutdown Initiation</b>: We call <code>executorService.shutdown()</code>, which initiates an orderly shutdown where previously submitted tasks are executed but no new tasks will be accepted.</li><li><b>Await Termination</b>: We use <code>awaitTermination()</code> to wait for all tasks to complete within a specified timeout (5 seconds in this case). If tasks are still running after the timeout, we force shutdown using <code>shutdownNow()</code>.</li><li><b>Handling InterruptedException</b>: If the current thread is interrupted while waiting, we catch the <code>InterruptedException</code> and call <code>shutdownNow()</code> to ensure the executor service stops.</li><li><b>Final Output</b>: After shutdown, we confirm that the executor service has been shut down.</li></ol><h2 id="8e96">Example 6 : Different types of thread pools and their specific behavior</h2><p id="4eed">Here’s an example covering the different types of thread pools provided by the <code>ExecutorService</code> in Java. We will create three types of thread pools: fixed thread pool, cached thread pool, and scheduled thread pool. Each will be used to demonstrate their specific behavior.</p><div id="239a"><pre>import java.util.concurrent.*;

<span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title">ThreadPoolTypesExample</span> {

<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">main</span>(<span class="hljs-params">String[] args</span>)</span> {
    <span class="hljs-comment">// Fixed Thread Pool</span>
    System.<span class="hljs-keyword">out</span>.println(<span class="hljs-string">"Fixed Thread Pool:"</span>);
    ExecutorService fixedThreadPool = Executors.newFixedThreadPool(<span class="hljs-number">2</span>);
    submitTasks(fixedThreadPool, <span class="hljs-number">5</span>);
    fixedThreadPool.shutdown();
    awaitTermination(fixedThreadPool);

    <span class="hljs-comment">// Cached Thread Pool</span>
    System.<span class="hljs-keyword">out</span>.println(<span class="hljs-string">"\nCached Thread Pool:"</span>);
    ExecutorService cachedThreadPool = Executors.newCachedThreadPool();
    submitTasks(cachedThreadPool, <span class="hljs-number">5</span>);
    cachedThreadPool.shutdown();
    awaitTermination(cachedThreadPool);

    <span class="hljs-comment">// Scheduled Thread Pool</span>
    System.<span class="hljs-keyword">out</span>.println(<span class="hljs-string">"\nScheduled Thread Pool:"</span>);
    ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(<span class="hljs-number">2</span>);
    <span class="hljs-keyword">for</span> (<span class="hljs-built_in">int</span> i = <span class="hljs-number">1</span>; i &lt;= <span class="hljs-number">5</span>; i++) {
        scheduledThreadPool.schedule(() -&gt; {
            System.<span class="hljs-keyword">out</span>.println(<span class="hljs-string">"Scheduled task executed by thread: "</span> + Thread.currentThread().getName());
        }, i, TimeUnit.SECONDS);
    }
    scheduledThreadPool.shutdown();
    awaitTermination(scheduledThreadPool);
}

<span class="hljs-function"><span class="hljs-keyword">private</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">submitTasks</span>(<span class="hljs-params">ExecutorService executorService, <span class="hljs-built_in">int</span> taskCount</span>)</span> {
    <span class="hljs-keyword">for</span> (<span class="hljs-built_in">int</span> i = <span class="hljs-number">1</span>; i &lt;= taskCount; i++) {
        executorService.submit(() -&gt; {
            System.<span class="hljs-keyword">out</span>.println(<span class="hljs-string">"Task executed by thread: "</span> + Thread.currentThread().getName());
            <span class="hljs-keyword">try</span> {
                Thread.sleep((<span class="hljs-built_in">long</span>) (Math.random() * <span class="hljs-number">2000</span>)); <span class="hljs-comment">// Simulate variable work</span>
            } <span class="hljs-keyword">catch</span> (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
            System.<span class="hljs-keyword">out</span>.println(<span class="hljs-string">"Task completed by thread: "</span> + Thread.currentThread().getName());
        });
    }
}

<span class="hljs-function"><span class="hljs-keyword">private</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">awaitTermination</span>(<span class="hljs-params">ExecutorService executorService</span>)</span> {
    <span class="hljs-keyword">try</span> {
        <span class="hljs-keyword">if</span> (!executorService.awaitTermination(<span class="hljs-number">5</span>, TimeUnit.SECONDS)) {
            System.<span class="hljs-keyword">out</span>.println(<span class="hljs-string">"Forcing shutdown..."</span>);
            executorService.shutdownNow();
        }
    } <span class="hljs-keyword">catch</span> (InterruptedException e) {
        System.err.println(<span class="hljs-string">"Termination interrupted!"</span>);
        executorService.shutdownNow();
    }
}

}</pre></div><p id="8540">Output :</p><div id="dfe1"><pre><span class="hljs-attr">Fixed Thread Pool:</span> <span class="hljs-attr">Task executed by thread:</span> <span class="hljs-string">pool-1-thread-1</span> <span class="hljs-attr">Task executed by thread:</span> <span class="hljs-string">pool-1-thread-2</span> <span class="hljs-attr">Task completed by thread:</span> <span class="hljs-string">pool-1-thread-1</span> <span class="hljs-attr">Task executed by thread:</span> <span class="hljs-string">pool-1-thread-1</span> <span class="hljs-attr">Task completed by thread:</span> <span class="hljs-string">pool-1-thread-2</span> <span class="hljs-attr">Task completed by thread:</span> <span class="hljs-string">pool-1-thread-1</span> <span class="hljs-attr">Task executed by thread:</span> <span class="hljs-string">pool-1-thread-2</span> <span class="hljs-attr">Task completed by thread:</span> <span class="hljs-string">pool-1-thread-2</span> <span class="hljs-attr">Task executed by thread:</span> <span class="hljs-string">pool-1-thread-1</span> <span class="hljs-attr">Task completed by thread:</span> <span class="hljs-string">pool-1-thread-1</span>

<span class="hljs-attr">Cached Thread Pool:</span> <span class="hljs-attr">Task executed by thread:</span> <span class="hljs-string">pool-2-thread-1</span> <span class="hljs-attr">Task executed by thread:</span> <span class="hljs-string">pool-2-thread-2</span> <span class="hljs-attr">Task executed by thread:</span> <span class="hljs-string">pool-2-thread-3</span> <span class="hljs-attr">Task executed by thread:</span> <span class="hljs-string">pool-2-thread-4</span> <span class="hljs-attr">Task completed by thread:</span> <span class="hljs-string">pool-2-thread-2</span> <span class="hljs-attr">Task completed by thread:</span> <span class="hljs-string">pool-2-thread-1</span> <span class="hljs-attr">Task completed by thread:</span> <span class="hljs-string">pool-2-thread-3</span> <span class="hljs-attr">Task completed by thread:</span> <span class="hljs-string">pool-2-thread-4</span> <span class="hljs-attr">Task executed by thread:</span> <span class="hljs-string">pool-2-thread-1</span> <span class="hljs-attr">Task completed by thread:</span> <span class="hljs-string">pool-2-thread-1</span>

<span class="hljs-attr">Scheduled Thread Pool:</span> <span class="hljs-attr">Scheduled task executed by thread:</span> <span class="hljs-string">pool-3-thread-1</span> <span class="hljs-attr">Scheduled task executed by thread:</span> <span class="hljs-string">pool-3-thread-2</span> <span class="hljs-attr">Scheduled task executed by thread:</span> <span class="hljs-string">pool-3-thread-1</span> <span class="hljs-attr">Scheduled task executed by thread:</span> <span class="hljs-string">pool-3-thread-2</span> <span class="hljs-attr">Scheduled task executed by thread:</span> <span class="hljs-string">pool-3-thread-1</span></pre></div><blockquote id="b616"><p><i>👉 <a href="https://readmedium.com/multithreading-in-java-f4180f8fe50f"><b>A Complete Overview of Multithreading Concepts and Practices — click the link to read more</b></a><b> </b>👈</i></p></blockquote><blockquote id="99cb"><p><i>👏<b> If you found my articles useful, please consider giving it claps and sharing it with your friends and colleagues.</b></i></p></blockquote><p id="ecbf"><b>To read other topics</b></p><ul><li><a href="https://readmedium.com/spring-boot-circuit-breaker-example-with-resilience4j-step-by-step-guide-e4c6f82711e5"><b>Spring Boot Circuit Breaker Example with Resilience4j: Step-by-Step Guide</b></a></li><li><a href="https://readmedium.com/spring-boot-retry-pattern-example-with-resilience4j-step-by-step-guide-91f98b382b35"><b>Spring Boot Retry Pattern Example with Resilience4j: Step-by-Step Guide</b></a></li><li><a href="https://readmedium.com/method-references-in-java-8-4ee9602922ea"><b>Method References in Java 8</b></a></li><li><a href="https://readmedium.com/transactional-annotation-in-spring-data-jpa-9f803d341289"><b>Mastering Transaction Propagation and Isolation in Spring Boot</b></a></li><li><a href="https://readmedium.com/using-hibernate-formula-in-spring-boot-to-compute-derived-fields-dynamically-21006573a82c"><b>@Formula Annotation in Spring Boot</b></a></li><li><a href="https://readmedium.com/understanding-logging-in-spring-boot-a-complete-overview-with-example-32b5cdfb16b8"><b>Understanding Logging in Spring Boot: A Complete Overview with Example</b></a></li><li><a href="https://readmedium.com/understanding-hash-collisions-in-javas-hashmap-24ed1856e71d"><b>Understanding Hash Collisions in Java’s HashMap</b></a></li><li><a href="https://readmedium.com/exploring-java-collections-a-guide-to-lists-sets-queues-and-maps-a1c06a9f674c"><b>Exploring Java Collections: A Guide to Lists, Sets, Queues, and Maps</b></a></li><li><a href="https://readmedium.com/using-the-temporal-annotation-in-hibernate-and-spring-boot-f8b46679af1d"><b>Using the @Temporal Annotation in Hibernate and Spring Boot</b></a></li><li><a href="https://readmedium.com/understanding-cors-and-csrf-in-spring-boot-7a2d92259592"><b>Understanding CORS and CSRF in Spring Boot</b></a></li><li><a href="https://readmedium.com/complete-crud-example-in-spring-boot-with-dto-validation-and-common-api-response-using-mysql-222679cbe4d5"><b>Complete CRUD Example in Spring Boot with DTO Validation, and Common API Response using MySQL</b></a></li><li><a href="https://readmedium.com/guide-to-spring-boot-validation-annotations-07b95963eac0"><b>Guide to Spring Boot Validation Annotations</b></a></li><li><a href="https://readmedium.com/mastering-git-and-github-integration-in-intellij-idea-a-complete-guide-to-version-control-7cf68cd7951a"><b>Mastering Git and GitHub Integration in IntelliJ IDEA</b></a></li><li><a href="https://readmedium.com/spring-boot-profiles-3df0345505f3"><b>Spring Boot Profiles</b></a></li><li><a href="https://readmedium.com/design-pattern-in-java-14289cc56f5b"><b>Design Pattern in java</b></a></li></ul></article></body>

Comprehensive Guide to Java Concurrency: ExecutorService, Thread Pools, Future Interface, Runnable, and Callable

👉 A Complete Overview of Multithreading Concepts and Practices — click the link to read more 👈

In this article, topics are covered.

  1. What is ExecutorService?
  2. Why ExecutorService?
  3. What is a Thread Pool?
  4. Why Use a Thread Pool?
  5. How Does a Thread Pool Work?
  6. Types of Thread Pools in Java[ Single Thread Executor, Fixed Thread Pool, Cached Thread Pool, Scheduled Thread Pool ].
  7. Advantages of Thread Pool
  8. Methods of ExecutorService[ submit(Runnable task), submit(Callable<T> task), invokeAll(Callable<T> tasks), invokeAny(Callable<T> tasks), shutdown(), shutdownNow(), isShutdown(), isTerminated() ].
  9. Future Interface
  10. Runnable Interface
  11. Callable Interface
  12. Differences Between Runnable and Callable
  13. Examples [ExecutorService, different methods used in ExecutorService, Future Interface, Runnable Interface, Callable Interface, Different types of threadPoolExecutor].

Here, we shall first grasp theoretically before delving extensively into several examples.

In Java, an ExecutorService is a part of the java.util.concurrent package and is a higher-level replacement for managing threads. It provides a more flexible and manageable way to handle asynchronous tasks compared to manually creating and managing threads. ExecutorService is an interface that is a sub-interface of the Executor interface and provides methods for executing, scheduling, and managing tasks concurrently.

Why ExecutorService?

  • Simplifies Thread Management: It abstracts away the complexities of managing thread creation, execution, and termination.
  • Thread Pooling: By using a pool of threads, it reuses threads for multiple tasks, improving resource management and performance, especially in multi-core systems.
  • Task Scheduling: It provides methods to submit tasks and retrieve results (e.g., Future), handle task timeouts, and schedule tasks periodically.
  • Graceful Shutdown: It offers mechanisms to stop or shut down the service in a controlled manner, ensuring tasks complete or terminate gracefully.

Use Case Scenario

Imagine you have a web application that processes a large number of user requests, such as fetching data from an API. Instead of creating a new thread for each request, you can use ExecutorService to efficiently manage and reuse threads.

Consider a scenario where you need to fetch data from multiple APIs simultaneously. Using ExecutorService, you can submit multiple API call tasks, and it will manage them using a pool of threads.

import java.util.concurrent.*;

public class ApiRequestProcessor {

    // Create a fixed thread pool with 4 threads
    private static final ExecutorService executorService = Executors.newFixedThreadPool(4);

    public static void main(String[] args) {
        // Create tasks to handle multiple API requests
        Callable<String> apiRequest1 = () -> {
            // Simulating an API request
            Thread.sleep(2000); // Simulates delay in API response
            return "Response from API 1";
        };

        Callable<String> apiRequest2 = () -> {
            Thread.sleep(1000); // Simulates delay in API response
            return "Response from API 2";
        };

        Callable<String> apiRequest3 = () -> {
            Thread.sleep(3000); // Simulates delay in API response
            return "Response from API 3";
        };

        // Submit tasks to the ExecutorService
        Future<String> future1 = executorService.submit(apiRequest1);
        Future<String> future2 = executorService.submit(apiRequest2);
        Future<String> future3 = executorService.submit(apiRequest3);

        try {
            // Get the results (blocking until they are done)
            System.out.println(future1.get());
            System.out.println(future2.get());
            System.out.println(future3.get());
        } catch (InterruptedException | ExecutionException e) {
            e.printStackTrace();
        } finally {
            // Shut down the ExecutorService gracefully
            executorService.shutdown();
        }
    }
}

Output

Response from API 1
Response from API 2
Response from API 3

Explanation:

  1. Fixed Thread Pool: We create a fixed thread pool of 4 threads using Executors.newFixedThreadPool(4).
  2. Callable Tasks: Three API call tasks are defined using Callable<String> which simulates delays using Thread.sleep().
  3. Submitting Tasks: We submit the tasks to the ExecutorService, which manages the execution of tasks in the background using pooled threads.
  4. Future: We use Future to get the result of each task when it completes. The get() method blocks until the task is done.
  5. Graceful Shutdown: After submitting all tasks and receiving the results, the ExecutorService is shut down to release resources.

What is a Thread Pool?

A Thread Pool is a group (or pool) of pre-initialized, reusable threads that are maintained and managed for performing tasks. Instead of creating and destroying threads for every single task, a thread pool reuses a fixed number of threads to execute multiple tasks concurrently. Once a thread completes a task, it becomes available for the next task, which helps in reducing the overhead of frequent thread creation and destruction.

Thread pooling is an important concept in concurrent programming and is commonly used to improve performance by limiting the number of concurrent threads and managing their lifecycle efficiently.

Key Characteristics of a Thread Pool:

  1. Fixed Number of Threads: A thread pool has a fixed number of threads (though this can be dynamic, depending on the implementation). This means that at any given time, a maximum number of threads will be actively executing tasks.
  2. Task Queue: If all threads are busy, any additional tasks that are submitted are placed in a queue. These tasks wait until a thread becomes available.
  3. Thread Reuse: Once a thread completes a task, it does not terminate. Instead, it is returned to the pool and becomes available to pick up new tasks. This reuse of threads minimizes the overhead associated with thread creation and destruction.
  4. Task Scheduling: Thread pools manage task execution by assigning tasks to available threads. The pool takes care of starting the threads, running the tasks, and reassigning threads once tasks are completed.
  5. Efficient Resource Usage: By reusing threads and controlling the number of threads executing concurrently, a thread pool ensures that resources like CPU and memory are not overburdened by excessive thread creation.

Why Use a Thread Pool?

  1. Thread Creation Overhead: Creating a new thread is costly in terms of time and system resources. By reusing threads, the thread pool reduces the overhead associated with thread creation and destruction.
  2. Limits the Number of Threads: A system can be overwhelmed if too many threads are created (leading to thread contention and excessive context switching). By limiting the number of threads, a thread pool prevents resource exhaustion and ensures that the system remains responsive.
  3. Task Management: With a thread pool, you don’t need to worry about manually starting or stopping threads. You simply submit tasks, and the thread pool manages the execution, queuing, and reuse of threads.
  4. Scalability: Thread pools help scale applications to handle many tasks by efficiently managing the available threads, queuing excess tasks, and avoiding the pitfalls of thread proliferation.

How Does a Thread Pool Work?

  1. Task Submission: Tasks are submitted to the thread pool. These tasks can be Runnable or Callable objects.
  2. Task Queueing: If all threads in the pool are currently busy executing tasks, the newly submitted task is placed in a queue (typically a blocking queue like LinkedBlockingQueue or ArrayBlockingQueue).
  3. Task Execution by Worker Threads: If a thread is available, it picks up the task from the queue and starts executing it.
  4. Completion and Reuse: After a thread completes its task, it does not die. Instead, it becomes available again to execute another task from the queue.
  5. Shutdown: When the application is done using the thread pool, you can call the shutdown() method on the pool. The pool stops accepting new tasks and waits for currently running tasks to finish.

Types of Thread Pools in Java:

Java’s Executors class provides different types of thread pools depending on your use case:

  1. Fixed Thread Pool: Creates a pool with a fixed number of threads. This is suitable when the number of tasks is known in advance, and you want to restrict the number of concurrent threads.
ExecutorService fixedPool = Executors.newFixedThreadPool(4);

2. Cached Thread Pool: Creates a pool that creates new threads as needed but reuses previously created threads when they are available. This is suitable for short-lived tasks.

ExecutorService cachedPool = Executors.newCachedThreadPool();

3. Single Thread Executor: Creates a pool with a single thread. This is useful when you need to guarantee that tasks are executed sequentially.

ExecutorService singleThreadPool = Executors.newSingleThreadExecutor();

4. Scheduled Thread Pool: Creates a pool of threads that can schedule tasks to execute after a delay or periodically.

ScheduledExecutorService scheduledPool = Executors.newScheduledThreadPool(2);

Advantages of Thread Pool:

  1. Improved Performance: By reusing existing threads and controlling the number of concurrent threads, thread pools can improve performance by reducing the overhead of creating and destroying threads.
  2. Better Resource Management: Thread pools allow you to control the number of active threads, preventing resource exhaustion in environments with limited CPU and memory.
  3. Simplified Concurrency: Instead of managing threads manually, you can focus on the tasks themselves. The thread pool handles scheduling, execution, and concurrency management.
  4. Scalability: Thread pools are well-suited for handling a large number of tasks and can easily scale across multiple cores in multi-core processors.

Disadvantages of Thread Pool:

  1. Potential for Task Delay: If the number of tasks exceeds the pool size, tasks will be queued, and some tasks might experience delays.
  2. Deadlocks and Resource Contention: If a task in the pool is waiting for another task to finish (e.g., by joining a thread), it can lead to a deadlock if all threads are busy.
  3. Task Rejection: If the thread pool’s queue is full and no threads are available, tasks may be rejected. You need to handle such scenarios carefully.

Creating a Thread Pool:

Here is a basic example of how a thread pool works in Java:

import java.util.concurrent.*;

public class ThreadPoolExample {
    public static void main(String[] args) {
        // Creating a fixed thread pool with 3 threads
        ExecutorService threadPool = Executors.newFixedThreadPool(3);

        // Submitting multiple tasks to the thread pool
        for (int i = 0; i < 10; i++) {
            threadPool.submit(new Task(i));
        }

        // Shutdown the thread pool (no more tasks will be accepted)
        threadPool.shutdown();
    }
}

class Task implements Runnable {
    private int taskId;

    public Task(int taskId) {
        this.taskId = taskId;
    }

    @Override
    public void run() {
        System.out.println("Task " + taskId + " is being executed by " +
                           Thread.currentThread().getName());
        try {
            Thread.sleep(2000); // Simulating work with sleep
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

Explanation of Code:

  1. Fixed Thread Pool: We create a thread pool with 3 threads using Executors.newFixedThreadPool(3). This means that at any given time, only 3 threads will be executing tasks concurrently.
  2. Task Submission: We submit 10 tasks to the thread pool. Each task is an instance of the Task class, which implements the Runnable interface. Each task sleeps for 2 seconds to simulate some work.
  3. Thread Reuse: As there are only 3 threads in the pool, the tasks are queued once all 3 threads are busy. As soon as a thread finishes executing a task, it picks up the next task from the queue.
  4. Shutdown: After submitting all tasks, we call shutdown() on the ExecutorService to prevent new tasks from being accepted. However, the already submitted tasks will continue to execute until completion.

Methods of ExecutorService:

Here is a detailed explanation of the types of methods provided by ExecutorService:

1. Task Submission Methods

These methods are used to submit tasks for execution, either immediately or after a delay.

  • submit(Runnable task) Submits a Runnable task for execution and returns a Future<?> representing the result of the task.
  • submit(Callable<T> task) Submits a Callable task for execution and returns a Future<T> representing the result of the task. The Callable allows you to return a value from the task.
  • submit(Runnable task, T result) Submits a Runnable task and returns a Future<T> that will return the provided result (T) upon completion.

2. Invoke Methods

These methods allow the submission of multiple tasks at once and provide different ways to retrieve their results.

  • invokeAll(Collection<? extends Callable<T>> tasks) Submits a collection of Callable tasks and returns a list of Future<T> objects representing the status of each task. The method blocks until all tasks are complete.
  • invokeAll(Collection<? extends Callable<T>> tasks, long timeout, TimeUnit unit) Submits a collection of Callable tasks with a time limit, and returns a list of Future<T>. Tasks that don't complete within the specified time are canceled.
  • invokeAny(Collection<? extends Callable<T>> tasks) Submits a collection of Callable tasks and returns the result of one successfully completed task (whichever completes first). It cancels the other tasks.
  • invokeAny(Collection<? extends Callable<T>> tasks, long timeout, TimeUnit unit) Similar to invokeAny(), but with a timeout. Returns the result of one task if it completes within the specified time limit; otherwise, throws a TimeoutException.

3. Shutdown and Termination Methods

These methods control the lifecycle of the ExecutorService, allowing you to gracefully or forcibly shut down the pool of threads.

  • shutdown() Initiates an orderly shutdown of the ExecutorService. It will no longer accept new tasks, but will allow previously submitted tasks to finish execution.
  • shutdownNow() Immediately attempts to stop all actively executing tasks and halts the processing of waiting tasks. It returns a list of tasks that were waiting to be executed.
  • isShutdown() Returns true if the ExecutorService has been shut down either via shutdown() or shutdownNow().
  • isTerminated() Returns true if all tasks have completed following a shutdown. If tasks are still running, this returns false.
  • awaitTermination(long timeout, TimeUnit unit) Blocks until all tasks have completed after a shutdown request, or the timeout occurs, or the current thread is interrupted, whichever happens first. This helps ensure that all tasks finish within a specific time frame.

What is Future interface and runnable and callable?

1. Future Interface

The Future interface in Java represents the result of an asynchronous computation. It provides methods to check the status of the computation, retrieve the result when it’s ready, and cancel the task if necessary.

Key Features of Future:

  • Handles Asynchronous Computation: A Future is used to retrieve the result of a task that is executed asynchronously (i.e., in a different thread).
  • Task Status: It can check whether the task is completed, canceled, or still in progress.
  • Task Cancellation: You can cancel the task if it hasn’t finished execution.
  • Blocking Get: It allows you to retrieve the result of the task, and this operation blocks the calling thread if the task is still running.

Common Methods of Future:

  • boolean cancel(boolean mayInterruptIfRunning) Attempts to cancel the execution of the task. If the task has already completed or was already canceled, it returns false. The mayInterruptIfRunning flag determines whether to interrupt the task if it's currently running.
  • boolean isCancelled() Returns true if the task was canceled before it completed normally.
  • boolean isDone() Returns true if the task has completed, either by normal termination or cancellation.
  • T get() Retrieves the result of the computation. This method blocks until the task is completed.
  • T get(long timeout, TimeUnit unit) Retrieves the result but waits only for the specified timeout before throwing a TimeoutException.

Example of Future:

import java.util.concurrent.*;

public class FutureExample {
    public static void main(String[] args) throws InterruptedException, ExecutionException {
        ExecutorService executor = Executors.newSingleThreadExecutor();
        
        // Submit a task that returns a result
        Future<Integer> future = executor.submit(() -> {
            Thread.sleep(2000); // Simulate long-running task
            return 10;
        });

        // Perform other operations while the task is running
        System.out.println("Task submitted, doing something else...");

        // Block and get the result once the task is done
        Integer result = future.get();
        System.out.println("Task completed, result: " + result);

        executor.shutdown();
    }
}

2. Runnable Interface

Runnable is one of the basic interfaces for representing a task that can be executed concurrently in a separate thread. A Runnable represents a task that does not return any result and cannot throw a checked exception.

Key Features of Runnable:

  • No Return Value: The run() method doesn’t return any value. It’s suitable for tasks that are executed for their side effects (e.g., updating a UI, processing files) rather than returning a result.
  • Can Be Used with Threads: Runnable can be passed to a Thread object, or it can be submitted to an ExecutorService.
  • Cannot Throw Checked Exceptions: It cannot throw any checked exceptions from the run() method, limiting its flexibility compared to Callable.

Runnable Method:

  • void run(): The abstract method that must be implemented. This is the method that contains the code to be executed when the task is run.

Example of Runnable:

public class RunnableExample {
    public static void main(String[] args) {
        // Creating a Runnable task
        Runnable task = () -> {
            System.out.println("Executing task inside a thread: " + Thread.currentThread().getName());
        };

        // Executing the task using a Thread
        Thread thread = new Thread(task);
        thread.start();
    }
}

When to Use Runnable:

  • When you don’t need a result from the task.
  • When you don’t need to throw checked exceptions.
  • Suitable for simple background tasks.

3. Callable Interface

Callable is a similar interface to Runnable, but it’s more powerful. It allows you to return a result and also to throw checked exceptions. A Callable is often used when you need the result of the computation.

Key Features of Callable:

  • Return Value: The call() method returns a value (generic type V). This makes Callable more flexible than Runnable, which cannot return anything.
  • Can Throw Checked Exceptions: The call() method allows checked exceptions to be thrown. This makes it suitable for tasks where you might need to handle exceptions explicitly (e.g., handling I/O operations).
  • Used with ExecutorService: Callable is typically submitted to an ExecutorService, and its result can be retrieved using a Future.

Callable Method:

  • V call(): The abstract method that must be implemented. It contains the code to execute and returns a result.

Example of Callable:

import java.util.concurrent.*;

public class CallableExample {
    public static void main(String[] args) throws InterruptedException, ExecutionException {
        ExecutorService executor = Executors.newSingleThreadExecutor();
        
        // Creating a Callable task that returns a result
        Callable<Integer> task = () -> {
            System.out.println("Task is being executed");
            Thread.sleep(2000); // Simulate some work
            return 42;
        };

        // Submitting the task to ExecutorService and getting a Future
        Future<Integer> future = executor.submit(task);

        // Get the result of the task (this will block until the task is complete)
        Integer result = future.get();
        System.out.println("Task completed, result: " + result);

        executor.shutdown();
    }
}

When to Use Callable:

  • When you need to return a result from the task.
  • When the task might throw checked exceptions (e.g., when dealing with I/O, database, or network operations).
  • When using ExecutorService to handle the execution of background tasks and needing a result after task completion.

Key Differences Between Runnable and Callable

Usage of Future, Runnable, and Callable Together

  • Runnable can be used with an ExecutorService or Thread to execute a task that doesn't return a value.
  • Callable is typically used with an ExecutorService to execute tasks that return a result or might throw exceptions. When you submit a Callable to an ExecutorService, it returns a Future, which you can use to obtain the result of the Callable.

Example: Using Callable with Future:

ExecutorService executor = Executors.newFixedThreadPool(2);

// Submit a Callable task that returns a result
Future<String> future = executor.submit(() -> {
    Thread.sleep(1000);
    return "Task Completed";
});

// Do other work here while the task is being executed

// Block and retrieve the result
try {
    String result = future.get();
    System.out.println("Task result: " + result);
} catch (InterruptedException | ExecutionException e) {
    e.printStackTrace();
}

executor.shutdown();
  • Runnable is for tasks that don’t need to return a result.
  • Callable is for tasks that need to return a result and may throw exceptions.
  • Future is used to represent the result of an asynchronous task, allowing you to check the task’s status, retrieve its result, or cancel it if necessary.

Examples

1. ExecutorService using submit(Runnable task)

Here’s an example of using the ExecutorService with submit(Runnable task). In this example, we will create a thread pool with multiple threads using ExecutorService, submit several Runnable tasks for execution, and observe the output.

Use Case Scenario:

Suppose we are processing multiple orders concurrently in an e-commerce system, and each task represents the processing of a single order. Since we don’t need a return value, we’ll use Runnable.

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

public class OrderProcessingExample {

    // Simulate order processing
    public static class ProcessOrderTask implements Runnable {
        private final int orderId;

        public ProcessOrderTask(int orderId) {
            this.orderId = orderId;
        }

        @Override
        public void run() {
            System.out.println("Processing order: " + orderId + " by thread: " + Thread.currentThread().getName());
            // Simulate some processing time
            try {
                Thread.sleep(1000);  // Simulate time to process an order
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("Completed processing order: " + orderId);
        }
    }

    public static void main(String[] args) {
        // Create an ExecutorService with a fixed thread pool of 3 threads
        ExecutorService executorService = Executors.newFixedThreadPool(3);

        // Submit 5 order processing tasks to the executor
        for (int i = 1; i <= 5; i++) {
            executorService.submit(new ProcessOrderTask(i));
        }

        // Shutdown the executor service after tasks are submitted
        executorService.shutdown();
    }
}

Output:

Processing order: 1 by thread: pool-1-thread-1
Processing order: 2 by thread: pool-1-thread-2
Processing order: 3 by thread: pool-1-thread-3
Completed processing order: 1
Processing order: 4 by thread: pool-1-thread-1
Completed processing order: 2
Processing order: 5 by thread: pool-1-thread-2
Completed processing order: 3
Completed processing order: 4
Completed processing order: 5

Explanation:

  1. We create a fixed thread pool of 3 threads using Executors.newFixedThreadPool(3).
  2. We create and submit 5 Runnable tasks (ProcessOrderTask) to the ExecutorService. Each task simulates the processing of an order, represented by an order ID.
  3. Each task runs on a separate thread from the thread pool. Since we have a pool of 3 threads, the first 3 tasks will be processed simultaneously. The remaining tasks wait until a thread becomes available.
  4. After submitting all the tasks, we call shutdown() to prevent the ExecutorService from accepting new tasks and ensure that it shuts down after completing the currently submitted tasks.

2. ExecutorService using submit(Callable<T> task)

Here’s an example of using the ExecutorService with submit(Callable<T> task). In this example, we will create a thread pool, submit several Callable tasks, and get their results using Future.

Use Case Scenario:

Suppose we are calculating the prices of items in an e-commerce system, where each task represents fetching the price of a single item. Since we need to return a result (the item price), we’ll use Callable.

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.List;
import java.util.ArrayList;

public class PriceCalculationExample {

    // Simulate price calculation task for an item
    public static class PriceTask implements Callable<Double> {
        private final int itemId;

        public PriceTask(int itemId) {
            this.itemId = itemId;
        }

        @Override
        public Double call() throws Exception {
            System.out.println("Fetching price for item: " + itemId + " by thread: " + Thread.currentThread().getName());
            // Simulate some processing time
            Thread.sleep(1000);  // Simulate time to calculate price
            double price = Math.random() * 100; // Simulate price between 0 and 100
            System.out.println("Price for item " + itemId + " is: $" + price);
            return price;
        }
    }

    public static void main(String[] args) throws Exception {
        // Create an ExecutorService with a fixed thread pool of 3 threads
        ExecutorService executorService = Executors.newFixedThreadPool(3);

        // Create a list to hold Future objects that store the results
        List<Future<Double>> futures = new ArrayList<>();

        // Submit 5 price calculation tasks to the executor
        for (int i = 1; i <= 5; i++) {
            Future<Double> future = executorService.submit(new PriceTask(i));
            futures.add(future);  // Store the future result
        }

        // Retrieve and display the results of each task
        for (int i = 0; i < futures.size(); i++) {
            Double price = futures.get(i).get();  // This will block until the result is available
            System.out.println("Final price for item " + (i + 1) + " is: $" + price);
        }

        // Shutdown the executor service after tasks are submitted
        executorService.shutdown();
    }
}

Output:

Fetching price for item: 1 by thread: pool-1-thread-1
Fetching price for item: 2 by thread: pool-1-thread-2
Fetching price for item: 3 by thread: pool-1-thread-3
Price for item 1 is: $32.3548353899836
Fetching price for item: 4 by thread: pool-1-thread-1
Price for item 2 is: $25.243423111847564
Fetching price for item: 5 by thread: pool-1-thread-2
Price for item 3 is: $57.83639876554309
Price for item 4 is: $41.63741232512334
Price for item 5 is: $11.482103439847643
Final price for item 1 is: $32.3548353899836
Final price for item 2 is: $25.243423111847564
Final price for item 3 is: $57.83639876554309
Final price for item 4 is: $41.63741232512334
Final price for item 5 is: $11.482103439847643

Explanation:

  1. Thread Pool Creation: We create a fixed thread pool with 3 threads using Executors.newFixedThreadPool(3).
  2. Callable Task (PriceTask): We define a Callable task (PriceTask) that simulates fetching the price of an item by waiting for 1 second and then generating a random price between 0 and 100.
  3. Submit Callable Tasks: We submit 5 PriceTask objects to the ExecutorService. Each task will calculate the price for one item, and the result will be stored in a Future object.
  4. Future Handling: We store the Future objects in a list and then call get() on each future to retrieve the calculated price. The get() method blocks until the price is available, ensuring we get the correct result.
  5. Shutdown: After submitting all the tasks, we call shutdown() on the ExecutorService to ensure no new tasks are accepted and the service shuts down after completing the submitted tasks.

3. ExecutorService using invokeAll(Callable<T> task)

Here’s an example of using ExecutorService with the invokeAll(Collection<? extends Callable<T>> tasks) method in Java. This method executes a collection of Callable tasks and returns a list of Future objects representing the results of the tasks. The invokeAll method blocks until all tasks have completed.

Use Case Scenario:

Let’s imagine we are fetching data from multiple remote APIs simultaneously. Each API call is simulated as a separate Callable task. We want to execute all API calls concurrently and retrieve the results once all tasks are complete.

import java.util.concurrent.*;
import java.util.List;
import java.util.ArrayList;

public class APIDataFetchExample {

    // Simulate API data fetching task
    public static class FetchDataTask implements Callable<String> {
        private final String apiName;

        public FetchDataTask(String apiName) {
            this.apiName = apiName;
        }

        @Override
        public String call() throws Exception {
            System.out.println("Fetching data from: " + apiName + " by thread: " + Thread.currentThread().getName());
            // Simulate some delay in fetching data
            Thread.sleep((long) (Math.random() * 2000));  // Simulate variable time for each API call
            String result = apiName + " data";
            System.out.println("Data fetched from " + apiName);
            return result;
        }
    }

    public static void main(String[] args) throws InterruptedException, ExecutionException {
        // Create an ExecutorService with a fixed thread pool of 3 threads
        ExecutorService executorService = Executors.newFixedThreadPool(3);

        // Create a list of Callable tasks to fetch data from multiple APIs
        List<Callable<String>> apiTasks = new ArrayList<>();
        apiTasks.add(new FetchDataTask("API_1"));
        apiTasks.add(new FetchDataTask("API_2"));
        apiTasks.add(new FetchDataTask("API_3"));
        apiTasks.add(new FetchDataTask("API_4"));
        apiTasks.add(new FetchDataTask("API_5"));

        // Execute all tasks and wait for them to complete using invokeAll
        List<Future<String>> results = executorService.invokeAll(apiTasks);

        // Process the results
        for (int i = 0; i < results.size(); i++) {
            String result = results.get(i).get();  // This will block until the task is done
            System.out.println("Result of API task " + (i + 1) + ": " + result);
        }

        // Shutdown the executor service
        executorService.shutdown();
    }
}

Output:

Fetching data from: API_1 by thread: pool-1-thread-1
Fetching data from: API_2 by thread: pool-1-thread-2
Fetching data from: API_3 by thread: pool-1-thread-3
Data fetched from API_2
Fetching data from: API_4 by thread: pool-1-thread-2
Data fetched from API_3
Fetching data from: API_5 by thread: pool-1-thread-3
Data fetched from API_1
Data fetched from API_5
Data fetched from API_4
Result of API task 1: API_1 data
Result of API task 2: API_2 data
Result of API task 3: API_3 data
Result of API task 4: API_4 data
Result of API task 5: API_5 data

Explanation:

  1. Thread Pool Creation: We create a fixed thread pool with 3 threads using Executors.newFixedThreadPool(3).
  2. Callable Task (FetchDataTask): Each task simulates fetching data from an API. The call() method simulates a delay (random between 0 and 2000 milliseconds) to mimic different response times for each API.
  3. invokeAll: We use invokeAll() to submit all the tasks at once. This method blocks until all tasks are completed, meaning all data fetching operations will be executed concurrently by the thread pool.
  4. Result Processing: After all tasks are completed, we use Future.get() on each Future object in the result list to retrieve the data fetched by each API.
  5. Shutdown: Once all tasks are processed, we shut down the ExecutorService.

4. ExecutorService using invokeAny(Collection<? extends Callable<T>> tasks)

Here’s an example of using ExecutorService with the invokeAny(Collection<? extends Callable<T>> tasks) method in Java. This method executes a collection of Callable tasks and returns the result of the first successfully completed task. If any task throws an exception, it will be propagated, and execution will stop.

Use Case Scenario:

Let’s imagine we are trying to find the price of a product from multiple sources (APIs). We want to retrieve the price from the fastest source available. Each source is represented by a Callable task that simulates fetching the price.

import java.util.concurrent.*;
import java.util.ArrayList;
import java.util.List;

public class FastestPriceFetcherExample {

    // Simulate price fetching task from different sources
    public static class PriceFetchTask implements Callable<Double> {
        private final String sourceName;
        private final int delay; // Delay to simulate fetch time

        public PriceFetchTask(String sourceName, int delay) {
            this.sourceName = sourceName;
            this.delay = delay;
        }

        @Override
        public Double call() throws Exception {
            System.out.println("Fetching price from: " + sourceName + " (delay: " + delay + "ms)");
            // Simulate variable fetch time
            Thread.sleep(delay);
            double price = Math.random() * 100; // Simulate price between 0 and 100
            System.out.println("Price fetched from " + sourceName + ": $" + price);
            return price;
        }
    }

    public static void main(String[] args) {
        // Create an ExecutorService with a fixed thread pool of 3 threads
        ExecutorService executorService = Executors.newFixedThreadPool(3);

        // Create a list of Callable tasks with different delays
        List<Callable<Double>> priceFetchTasks = new ArrayList<>();
        priceFetchTasks.add(new PriceFetchTask("Source 1", 2000)); // 2 seconds delay
        priceFetchTasks.add(new PriceFetchTask("Source 2", 1000)); // 1 second delay
        priceFetchTasks.add(new PriceFetchTask("Source 3", 1500)); // 1.5 seconds delay
        priceFetchTasks.add(new PriceFetchTask("Source 4", 3000)); // 3 seconds delay

        try {
            // Invoke any task and get the result of the first one that completes
            Double fastestPrice = executorService.invokeAny(priceFetchTasks);
            System.out.println("Fastest fetched price: $" + fastestPrice);
        } catch (InterruptedException | ExecutionException e) {
            e.printStackTrace();
        } finally {
            // Shutdown the executor service
            executorService.shutdown();
        }
    }
}

Output

Fetching price from: Source 1 (delay: 2000ms)
Fetching price from: Source 2 (delay: 1000ms)
Fetching price from: Source 3 (delay: 1500ms)
Price fetched from Source 2: $45.25639845363515
Fastest fetched price: $45.25639845363515
Price fetched from Source 1: $73.48567579210142
Price fetched from Source 3: $34.83928121117716
Price fetched from Source 4: $29.00117935605395

Explanation:

  1. Thread Pool Creation: We create a fixed thread pool with 3 threads using Executors.newFixedThreadPool(3).
  2. Callable Task (PriceFetchTask): Each task simulates fetching a price from a source. The call() method includes a delay (simulating the time taken to fetch the price) and returns a randomly generated price.
  3. invokeAny: We use invokeAny() to submit all tasks at once. This method blocks until the first task completes successfully, returning the result of that task.
  4. Output: The output shows which task was executed first, along with the corresponding price fetched from that source. If multiple tasks complete, only the result of the first one that finished successfully is returned.
  5. Shutdown: After obtaining the result, we shut down the ExecutorService.

5. ExecutorService using Shutdown and Termination Methods

Here’s an example of using the shutdown and termination methods of ExecutorService in Java. This example will demonstrate how to gracefully shut down an executor service and ensure that all tasks have completed before the program exits.

Use Case Scenario:

Let’s consider a scenario where we are processing a list of tasks that simulate downloading files. We want to ensure that all downloads are completed before the application shuts down.

import java.util.concurrent.*;

public class FileDownloadExample {

    // Simulate a file download task
    public static class DownloadTask implements Runnable {
        private final String fileName;

        public DownloadTask(String fileName) {
            this.fileName = fileName;
        }

        @Override
        public void run() {
            System.out.println("Starting download: " + fileName + " by thread: " + Thread.currentThread().getName());
            // Simulate time taken to download a file
            try {
                Thread.sleep((long) (Math.random() * 3000)); // Random sleep between 0 and 3 seconds
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt(); // Restore interrupted status
            }
            System.out.println("Completed download: " + fileName);
        }
    }

    public static void main(String[] args) {
        // Create an ExecutorService with a fixed thread pool of 2 threads
        ExecutorService executorService = Executors.newFixedThreadPool(2);

        // Submit download tasks to the executor
        for (int i = 1; i <= 5; i++) {
            executorService.submit(new DownloadTask("File_" + i));
        }

        // Initiate shutdown of the executor service
        executorService.shutdown();
        System.out.println("Shutdown initiated. No new tasks will be accepted.");

        try {
            // Wait for existing tasks to terminate, with a timeout
            if (!executorService.awaitTermination(5, TimeUnit.SECONDS)) {
                System.out.println("Some tasks are still running after timeout. Forcing shutdown...");
                executorService.shutdownNow(); // Force shutdown if tasks are still running
            }
        } catch (InterruptedException e) {
            System.err.println("Shutdown interrupted!");
            executorService.shutdownNow(); // Force shutdown on interruption
        }

        System.out.println("Executor service has been shut down.");
    }
}

Output

Starting download: File_1 by thread: pool-1-thread-1
Starting download: File_2 by thread: pool-1-thread-2
Starting download: File_3 by thread: pool-1-thread-1
Completed download: File_2
Completed download: File_1
Starting download: File_4 by thread: pool-1-thread-2
Completed download: File_3
Completed download: File_4
Starting download: File_5 by thread: pool-1-thread-1
Completed download: File_5
Shutdown initiated. No new tasks will be accepted.
Executor service has been shut down.

Explanation:

  1. Thread Pool Creation: We create a fixed thread pool with 2 threads using Executors.newFixedThreadPool(2).
  2. Runnable Task (DownloadTask): Each task simulates downloading a file. The run() method includes a delay to simulate the time taken to download.
  3. Submitting Tasks: We submit 5 download tasks to the executor service.
  4. Shutdown Initiation: We call executorService.shutdown(), which initiates an orderly shutdown where previously submitted tasks are executed but no new tasks will be accepted.
  5. Await Termination: We use awaitTermination() to wait for all tasks to complete within a specified timeout (5 seconds in this case). If tasks are still running after the timeout, we force shutdown using shutdownNow().
  6. Handling InterruptedException: If the current thread is interrupted while waiting, we catch the InterruptedException and call shutdownNow() to ensure the executor service stops.
  7. Final Output: After shutdown, we confirm that the executor service has been shut down.

Example 6 : Different types of thread pools and their specific behavior

Here’s an example covering the different types of thread pools provided by the ExecutorService in Java. We will create three types of thread pools: fixed thread pool, cached thread pool, and scheduled thread pool. Each will be used to demonstrate their specific behavior.

import java.util.concurrent.*;

public class ThreadPoolTypesExample {

    public static void main(String[] args) {
        // Fixed Thread Pool
        System.out.println("Fixed Thread Pool:");
        ExecutorService fixedThreadPool = Executors.newFixedThreadPool(2);
        submitTasks(fixedThreadPool, 5);
        fixedThreadPool.shutdown();
        awaitTermination(fixedThreadPool);

        // Cached Thread Pool
        System.out.println("\nCached Thread Pool:");
        ExecutorService cachedThreadPool = Executors.newCachedThreadPool();
        submitTasks(cachedThreadPool, 5);
        cachedThreadPool.shutdown();
        awaitTermination(cachedThreadPool);

        // Scheduled Thread Pool
        System.out.println("\nScheduled Thread Pool:");
        ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(2);
        for (int i = 1; i <= 5; i++) {
            scheduledThreadPool.schedule(() -> {
                System.out.println("Scheduled task executed by thread: " + Thread.currentThread().getName());
            }, i, TimeUnit.SECONDS);
        }
        scheduledThreadPool.shutdown();
        awaitTermination(scheduledThreadPool);
    }

    private static void submitTasks(ExecutorService executorService, int taskCount) {
        for (int i = 1; i <= taskCount; i++) {
            executorService.submit(() -> {
                System.out.println("Task executed by thread: " + Thread.currentThread().getName());
                try {
                    Thread.sleep((long) (Math.random() * 2000)); // Simulate variable work
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                }
                System.out.println("Task completed by thread: " + Thread.currentThread().getName());
            });
        }
    }

    private static void awaitTermination(ExecutorService executorService) {
        try {
            if (!executorService.awaitTermination(5, TimeUnit.SECONDS)) {
                System.out.println("Forcing shutdown...");
                executorService.shutdownNow();
            }
        } catch (InterruptedException e) {
            System.err.println("Termination interrupted!");
            executorService.shutdownNow();
        }
    }
}

Output :

Fixed Thread Pool:
Task executed by thread: pool-1-thread-1
Task executed by thread: pool-1-thread-2
Task completed by thread: pool-1-thread-1
Task executed by thread: pool-1-thread-1
Task completed by thread: pool-1-thread-2
Task completed by thread: pool-1-thread-1
Task executed by thread: pool-1-thread-2
Task completed by thread: pool-1-thread-2
Task executed by thread: pool-1-thread-1
Task completed by thread: pool-1-thread-1

Cached Thread Pool:
Task executed by thread: pool-2-thread-1
Task executed by thread: pool-2-thread-2
Task executed by thread: pool-2-thread-3
Task executed by thread: pool-2-thread-4
Task completed by thread: pool-2-thread-2
Task completed by thread: pool-2-thread-1
Task completed by thread: pool-2-thread-3
Task completed by thread: pool-2-thread-4
Task executed by thread: pool-2-thread-1
Task completed by thread: pool-2-thread-1

Scheduled Thread Pool:
Scheduled task executed by thread: pool-3-thread-1
Scheduled task executed by thread: pool-3-thread-2
Scheduled task executed by thread: pool-3-thread-1
Scheduled task executed by thread: pool-3-thread-2
Scheduled task executed by thread: pool-3-thread-1

👉 A Complete Overview of Multithreading Concepts and Practices — click the link to read more 👈

👏 If you found my articles useful, please consider giving it claps and sharing it with your friends and colleagues.

To read other topics

Executorservice
Future Interface
Thread Pool
Callable
Runnable
Recommended from ReadMedium