Multi-Threading in JAVA using Callables
Code Craft Club
Posted on September 14, 2023
Introduction to Callables
In multi-threaded programming, we often encounter scenarios where tasks need to be executed concurrently, but they don't necessarily need to return any values. Think of these tasks as workers performing their jobs independently, like chefs work simultaneously to execute their tasks, and they don't need to return any specific information. In such cases, runnables
can be used where there is nothing to return.
However, there are situations where not only do we need tasks to run concurrently, but we also need them to return some valuable data upon completion. This is where the concept of Callables
comes into play.
Let’s understand with an example:
Online Shopping:
Imagine you are an online shopper placing multiple orders. You want to track the status of each order and get confirmation when each order is successfully processed. This is where the concept of Callable
comes in.
- Callable is like each online order you've placed. These are the tasks you care about because you want to track the status of your orders.
- Future is like the shipping confirmation email you receive for each order. When an order is shipped (the task is completed), you get an email (Future) that tells you the order's status and provides tracking information.
Using Callables in Multi-Threaded Programs
To incorporate Callables
into multi-threaded programs, you can follow these steps:
Step 1: Identify Tasks Needing Data Returns
Identify tasks
within your program that require data to be returned upon completion. These tasks are analogous to online shopping orders where you want tracking information.
Step 2: Create Callable Classes
For tasks that need to return data, create classes and implement the Callable
interface. This interface allows tasks to return results or throw exceptions, making it suitable for data-retrieval tasks.
import java.util.concurrent.Callable;
class OrderProcessingTask implements Callable<String> {
private String item;
public OrderProcessingTask(String item) {
this.item = item;
}
}
Step 3: Implement the call()
Method
Inside your Callable
class, implement the call()
method, which contains the code for the task and defines the return type for the method. This method represents the work that needs to be done and returns the result upon completion.
@Override
public String call() throws Exception {
// Simulate order processing
Thread.sleep(2000); // Simulating processing time
return "Order for " + item + " is processed.";
}
Step 4: Create a Main Class Using Executor Service
Create a main class that utilizes the ExecutorService
to execute the Callable tasks:
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
public class OnlineShoppingApp {
public static void main(String[] args) throws Exception {
ExecutorService executor = Executors.newFixedThreadPool(3);
// Create order processing tasks
Callable<String> order1 = new OrderProcessingTask("Product A");
Callable<String> order2 = new OrderProcessingTask("Product B");
Callable<String> order3 = new OrderProcessingTask("Product C");
// Submit tasks to the executor
Future<String> result1 = executor.submit(order1);
Future<String> result2 = executor.submit(order2);
Future<String> result3 = executor.submit(order3);
// Wait for tasks to complete and retrieve results
String status1 = result1.get();
String status2 = result2.get();
String status3 = result3.get();
System.out.println(status1);
System.out.println(status2);
System.out.println(status3);
// Shutdown the executor when done
executor.shutdown();
}
}
Understanding the use of Future
with Callable
In the main class, we use Future
objects to capture the results of the submitted Callable
tasks. A Future
represents the result of an asynchronous computation, in this case, the result of each order processing task.
Analogy:
In our example scenario of Online Shopping, each order represents a separate task, and we want to keep track of their status and delivery. So:
Callable
is analogous to an online shopping order. Each order represents a task that we want to track.Future
is like the order confirmation or tracking number we receive after placing an online order. It’s a reference to our order’s status and allows us to retrieve details when the order is processed.
Just like you can check the status of our online orders using the order number without waiting at the store, we can use Future<>
to check the status and retrieve results from Callable tasks without blocking the program.
Here's how this works:
-
executor.submit(callable)
returns aFuture
representing the result of the callable task. -
result.get()
is called to retrieve the result of the task.
This allows to efficiently manage tasks that return data in a multi-threaded program. The Future
provides a way to obtain the result of the task when it's ready, just like tracking online orders and receiving status updates when the orders are processed.
Closing Thoughts:
Thank you for reading our blog. We appreciate your time and interest in our content. If you have any questions, feedback, or topics you’d like us to cover in our future articles, please feel free to leave a comment below. Your input is valuable, and it helps us create content that matters to you.
Stay connected with us for more insightful articles on various topics related to technology, programming, and much more.
Happy coding!
Posted on September 14, 2023
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.