-1

I am trying to improve the performance of the spring applications which invokes about 8-10 queries and combined it will take about 15 to 120 seconds depending on the amount of data it queries for, i'm proposing a CompletableFuture / Future way of doing it in the Java 8. But, i have been stuck at the point where the main thread does not wait for the async threads to be completed. Thefollowing is the code which I have implemented so far.

I've contemplated to go back and implement it using the 'Future' and ThreadPoolExecutor and have a thread that waits for the completion of the spawned callable threads in the threadPoolExecutor to return the data.

Question: Any easy way to implement the CompletableFuture in java and the main thread waits for all the threads in the thread pool to be completed before returning the data to the client?

InvokeCallable .java

package net.sampleSpring.service;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Component;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.function.Supplier;

@Component
public class InvokeCallable {

    @Autowired
    private ApplicationContext context;

//    @Autowired
//    @Qualifier("threadPoolExecutor")
//    private ThreadPoolTaskExecutor executorThreadPool;


    public void invokeCallables() throws InterruptedException, ExecutionException {
        List<CompletableFuture<Integer>> lst = new ArrayList<>();
        CallableOne callableOne = context.getBean(CallableOne.class);
        CallableTwo callableTwo = context.getBean(CallableTwo.class);

        CompletableFuture<Integer> completableFuture1 = CompletableFuture.supplyAsync(new Supplier<Integer>() {
                                                                                          @Override
                                                                                          public Integer get() {
                                                                                              try {
                                                                                                  return callableOne.call();
                                                                                              } catch (Exception e) {
                                                                                                  e.printStackTrace();
                                                                                              }
                                                                                              return 1;
                                                                                          }
                                                                                      }
        );

        CompletableFuture<String> completableFuture2 = CompletableFuture.supplyAsync(new Supplier<String>() {
                                                                                         @Override
                                                                                         public String get() {
                                                                                             try {
                                                                                                 return callableTwo.call();
                                                                                             } catch (Exception e) {
                                                                                                 e.printStackTrace();
                                                                                             }
                                                                                             return "1";
                                                                                         }
                                                                                     }
        );

        CompletableFuture.allOf(completableFuture1, completableFuture2).thenApply(
                i -> {
                    System.out.println("Completed running the futures");
                    System.out.println("future 1" + completableFuture1.join().toString());
                    System.out.println("future 2" + completableFuture2.join().toLowerCase());
                    return i;
                }
        );

    }
}

CallableTwo.java

import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;

import java.util.concurrent.Callable;

@Component
@Scope("prototype")
public class CallableTwo {

    public String call() throws Exception {
        Thread.sleep(2000);
        return "1000";
    }
}

CallableOne.java

import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;

@Component
@Scope("prototype")
public class CallableOne {

    public Integer call() throws Exception {
        Thread.sleep(2000);
        return 1;
    }
}

sampleSpringResource.java Code that invokes the InvokeCallable.java using restful service

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.dao.DataAccessException;
import org.springframework.stereotype.Service;

import javax.inject.Inject;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.core.Response;

import java.util.concurrent.ExecutionException;

import static javax.ws.rs.core.MediaType.APPLICATION_JSON;
import static org.json.XMLTokener.entity;

/* ItemValue Web service
 *
 */
@Path("/")
@Service
public class sampleSpringResource {

    @Inject
    private InvokeCallable callable;

    private static final Logger LOG = LogManager.getLogger();

    @GET
    @Path("/changeme/")
    @Produces(APPLICATION_JSON)
    public Response getsampleSpring() throws ExecutionException, InterruptedException {            
        callable.invokeCallables();               

    }

}
Zeus
  • 6,386
  • 6
  • 54
  • 89

1 Answers1

0

Thanks Abhijit for pointing to the correct location. I have implemented it in the following way.

SampleSpringResourc.java

/* ItemValue Web service
 *
 */
@Path("/")
@Service
public class sampleSpringResource {

    @Inject
    private InvokeCallable callable;

    private static final Logger LOG = LogManager.getLogger();


    @GET
    @Path("/changeme/")
    @Produces(APPLICATION_JSON)
    public Response getsampleSpring() throws ExecutionException, InterruptedException {
        System.out.print("In getSampleSpring()..");
        try {
            callable.invokeCallables();
            System.out.println("returning from the result");
            return LOG.exit(Response.status(Response.Status.OK).entity("Success").build());
        } catch (DataAccessException ex) {
            return LOG.exit(Response.status(Response.Status.INTERNAL_SERVER_ERROR)
                    .entity(new ServiceError(ErrorCode.INTERNAL_SERVER_ERROR, ex.getMessage())).build());
        } catch (Exception ex) {
            return LOG.exit(Response.status(Response.Status.INTERNAL_SERVER_ERROR)
                    .entity(new ServiceError(ErrorCode.INTERNAL_SERVER_ERROR, ex.getMessage())).build());
        }

    }

}

InvokeCallable

@Component
public class InvokeCallable {

    @Autowired
    private ApplicationContext context;

    @Autowired
    private CallableTwo callableTwo;

    @Autowired
    private CallableOne callableOne;

    public void invokeCallables() throws Exception {
        List<CompletableFuture<Integer>> lst = new ArrayList<>();
        System.out.println("Callable one start " + new Date());
        CompletableFuture<Integer> completableFuture1 = callableOne.call();
        System.out.println("Callable one end " + new Date());

        System.out.println("Callable two start " + new Date());
        CompletableFuture<String> completableFuture2 = callableTwo.call();
        System.out.println("Callable two end " + new Date());

        CompletableFuture.allOf(completableFuture1, completableFuture2).join();
        System.out.println("Completable future all end " + new Date());
    }
}

CallableTwo

@Component
@Scope("prototype")
public class CallableTwo {

    @Async
    public CompletableFuture<String> call() throws Exception {
        Thread.sleep(2000);
        return CompletableFuture.completedFuture("1000");
    }
}

CallableOne

@Component
@Scope("prototype")
public class CallableOne {

    @Async
    public CompletableFuture<Integer> call() throws Exception {
        Thread.sleep(2000);
        return CompletableFuture.completedFuture(1);
    }
}

The logs below show that the merged time result of two thread is shown. Each thread take about 2 seconds.

Logs

2017-08-23 11:27:30,118 INFO  [stdout] (default task-4) In getSampleSpring()..Callable one start Wed Aug 23 11:27:30 CDT 2017

2017-08-23 11:27:30,124 INFO  [stdout] (default task-4) Callable one end Wed Aug 23 11:27:30 CDT 2017

2017-08-23 11:27:30,125 INFO  [stdout] (default task-4) Callable two start Wed Aug 23 11:27:30 CDT 2017

2017-08-23 11:27:30,126 INFO  [stdout] (default task-4) Callable two end Wed Aug 23 11:27:30 CDT 2017

2017-08-23 11:27:32,138 INFO  [stdout] (default task-4) Completable future all end Wed Aug 23 11:27:32 CDT 2017

2017-08-23 11:27:32,138 INFO  [stdout] (default task-4) returning from the result
Zeus
  • 6,386
  • 6
  • 54
  • 89