2

I have the following code that is making a call to two web services. Both web services return very large responses, so the response is taking quite a long time to return (one web service request is 8 seconds, the other is 12 seconds). The total execution time is 20 seconds as the requests are running in series and not parallel.

Is there any way I can modify my code to request the two web services asynchronously and be able to get the response processed in a time closer to 12 seconds than 20 seconds that it currently takes?

String listOfCities;
String listOfCountries; 

try {
    listOfCities = service.getListOfCities(host+"service/cities");
    listOfCountries = service.getListOfCountries(host+"service/countries");
} catch (Exception e) {
    log.error("Failed to read service: " + e);
}

** Thanks for the responses, I feel this isn't a duplicate as I wanted to stop the execution of the two threads I was executing until both received a result from both. The solutions below show that. **

user2254180
  • 844
  • 13
  • 30
  • 4
    Check out [this](https://stackoverflow.com/questions/1842734/how-to-asynchronously-call-a-method-in-java) question. – Master Yoda Jan 24 '18 at 15:39
  • 1
    You will need to create two threads, each one starting one of the services. Then your main-logic thread (the third) will wait and periodically check whether the other two threads have finished yet. If they did you can get their results and continue. Note that there are numerous of ways to realize this (especially after Java 8&9). I have no overview over which methods currently are considered best practice. – Zabuzard Jan 24 '18 at 15:39
  • Thank you Master Yoda - the only question I have is I don't want the application to execute any further lines until both services have been called and returned a response. How would I do this? – user2254180 Jan 24 '18 at 15:45
  • You could write a callback method that executes when both threads have finished running. Check [this question](https://stackoverflow.com/questions/17255498/implementing-callbacks-in-java-with-runnable) – Master Yoda Jan 24 '18 at 15:48
  • Look at [this link](https://stackoverflow.com/a/3376628/6296931) – Coder ACJHP Jan 24 '18 at 15:56

4 Answers4

8

I would try something simple, like CompletableFuture:

import java.util.concurrent.CompletableFuture;
...
final CompletableFuture<String> listOfCities = CompletableFuture.supplyAsync(() -> service.getListOfCities(...));
final CompletableFuture<String> listOfCountries = CompletableFuture.supplyAsync(() -> service. getListOfCountries(...));
final CompletableFuture<Void> allCompleted = CompletableFuture.allOf(listOfCities, listOfCountries);
allCompleted.thenRun(() -> {
    // whatever you want to do 
});

See these examples for reference.

esin88
  • 3,091
  • 30
  • 35
6

very simple implementation, For more advance you may want to take look at FutureTask

    List<Thread> threadList = new ArrayList<>();
    threadList.add(new Thread(new Runnable() {
        @Override
        public void run() {
            try {
                listOfCountries = service.getListOfCountries(host+"service/countries");
            } catch (Exception e) {
                log.error("Failed to read service: " + e);
            }
        }
    }));
    threadList.add(new Thread(new Runnable() {
        @Override
        public void run() {
            try {
                listOfCities = service.getListOfCities(host+"service/cities");
            } catch (Exception e) {
                log.error("Failed to read service: " + e);
            }
        }
    }));
    for (Thread t:threadList ){
        t.start();
    }
    for (Thread t:threadList ){
        try {
            t.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
    //after both finish proceeds from here

Note the Strings Should be defined more globally (class level, not local variables)

Sarel Foyerlicht
  • 907
  • 6
  • 11
2

Global variables of the class.

String listOfCities;
String listOfCountries; 

In the function, the methods would be called like this,

try {//t is the object of the class like (Test t = new Test();)
    new Thread(()-> t.listOfCities = service.getListOfCities(host+"service/cities");).start();
    new Thread(()-> t.listOfCountries = service.getListOfCountries(host+"service/countries");).start();
} catch (Exception e) {
    log.error("Failed to read service: " + e);
}

Code example https://ideone.com/wB9SMa

By @AniketSahrawat

Farhan Qasim
  • 990
  • 5
  • 18
  • That just won't work because of closure. – Aniket Sahrawat Jan 24 '18 at 15:44
  • @AniketSahrawat can you edit it to make it work? I am also new to java multi threading, your help will be very much grateful. If not can you please provide a good reference for it? – Farhan Qasim Jan 24 '18 at 15:46
  • 1
    I don't understand why this answer has been upvoted. `listOfCities` and `listOfCountries` are local variables, and this approach must fail because lambdas require the variable to be effectively final. @FarhanQasim If you are new to multi threading in Java then you should learn it first before answering, and btw the mistake in your answer is not related to multithreading but closures. – Aniket Sahrawat Jan 24 '18 at 16:04
  • 1
    Check [this](https://ideone.com/wB9SMa) approach vs [your approach](https://ideone.com/jTd8mg) – Aniket Sahrawat Jan 24 '18 at 16:10
  • @AniketSahrawat thank alot sir, i'll edit my answer now. – Farhan Qasim Jan 24 '18 at 16:12
0

If you want the execution time in completion order I would advice you to use ListenableFuture from guava. Futures.inCompletionOrder will do the job.

Sample usage can look something like that:

ExecutorService es;
Callable<String> task1;
Callable<String> task2;
//...
ListeningExecutorService listeningExecutorService = MoreExecutors.listeningDecorator(es);
List<ListenableFuture<String>> yourTasks = new ArrayList<>();
yourTasks.add(listeningExecutorService.submit(task1));
yourTasks.add(listeningExecutorService.submit(task2));
for(Future f: Futures.inCompletionOrder(yourTasks)){
   //process your task in completion order now
}
St.Antario
  • 26,175
  • 41
  • 130
  • 318