I have a for loop in which i make call to db to get some values of a student. Now for loop takes a lot of time. So thought to replace that with executor service with future objects. At the start of function i made calls to db using executor.submit()
method.
I am just not sure how to find the future objects returned is for that particular student which i can use for further steps. Please find below the code
Function below is getting called from a for loop which overall takes lot of time since getting called by each Student:
public void getDbDetails(String student){
//calling db
if(dbDetails.studentName = "XYZ"){
//function based on student ID
}
}
Using Executor Framework:
public void getStudentData(List<String> students){
final List<Future<?>> futures = new ArrayList<>();
for(String student: students){
Future<?> future = executor.submit(() ->{
//db call
});
futures.add(future);
}
}
public void getDbDetails(String student){
//In this case how would i know whether future has returned details for this particular student??
if(dbDetails.studentName = "XYZ"){
//function based on student ID
}
}
Is it possible to use future.get()
and create a map based on the studentId from future object and then use that map in function getDbDetails() to fetch particular student information and if future has not returned anything by that time then use Thread.wait();
? Is this approach right or anything else can be used as optimum solution?
CodePudding user response:
The main problem is that your return value on your task is void
. Instead you should be defining a Callable
that returns a value.
A rough draft of some untested code:
public class StudentNameLookup implements Callable < String >
{
private String studentId ;
public StudentNameLookup ( String studentId )
{
this.studentId = studentId ;
}
public String call()
{
String studentName ;
// … DB call, passing `this.studentId`, returning a value stored in `studentName` local var.
return studentName ;
}
}
In Java 16 we could use a record for brevity.
public record StudentNameLookup ( String studentId ) implements Callable < String >
{
public String call()
{
String studentName ;
// … DB call, passing `this.studentId`, returning a value stored in `studentName` local var.
return studentName ;
}
}
As a matter of taste, I would build up a list of tasks.
List < Callable < String > > tasks = new ArrayList<>();
for( String studentId : studentIds )
{
Callable < String > task = new StudentNameLookup( studentId ) ;
tasks.add( task ) ;
}
Submit all the tasks to an executor service. You get back a list of futures.
The parameterized type (angle brackets) of your futures will be the type of that return value you declared on your Callable
.
ExecutorService es = Executors. … ;
List < Future < String > > futures = es.invokeAll( tasks ) ;
Then do the usual executor service work. Do a shutdown, await termination. After that, you know all the submitted tasks have been processed.
Loop your list of futures. Test each future to see if it succeeded, then retrieve its result.
String studentName = future.get() ;
Read Javadoc and search Stack Overflow for boilerplate.
If you want to return the student id along with the retrieved student name, change your return type. Instead of returning a mere String
, return an object of a type you invent, a type that include both id and name.
public record Student ( String id , String name ) {}
Change the parameterized types (angle brackets) seen in code above from < String >
to < Student >
.