I am facing a very weird problem.
I am working on Vert.x and from handler I am calling REST APIs using HttpClientRequest of Vert.x. Now I am having a CompletableFuture which I am completing in the response handler of the HttpClientRequest. Later, I am using CompletableFuture.get(). But whenever get() method is called, the main thread is blocked (as expected), but it remains blocked forever. I am not seeing the callback happen on my response Handler and it is stuck for forever.
Here is code:
import io.vertx.core.http.HttpClientRequest;
import io.vertx.core.http.HttpMethod;
import io.vertx.core.json.Json;
import io.vertx.core.json.JsonObject;
import java.util.concurrent.CompletableFuture;
import io.vertx.core.http.HttpClient;
CompletableFuture<JsonObject> comp = new CompletableFuture<JsonObject>();
HttpClient httpClient = new HttpClient(); //This object initialized and set the endpoit, port and domain name.
HttpClientRequest request = httpClient.request(HttpMethod.POST, requestURI, response -> {
response.bodyHandler(body -> {
//do some process
comp.complete(new JsonObject(body);
});
}).exceptionHandler(e -> {
//log the error
comp.completeExceptionally(e);
});
request.end();
//after some process
comp.get(); // here main thread is stuck forever.
My API gives 200 response, I saw in it Wireshark and also If I do comp.thenAccept() the callback is executed and it gives my result.
Why is this happening and what is the solution?
Note: I know that it is not recommendation to use Completable.get() method but in my use-case, I have to use it.
Here is sample code which is giving me issue:
package io.vertx.starter;
import io.vertx.core.AbstractVerticle;
import io.vertx.core.Future;
import io.vertx.core.http.*;
import io.vertx.ext.web.Router;
import io.vertx.ext.web.RoutingContext;
import java.util.concurrent.CompletableFuture;
public class SampleVerticle extends AbstractVerticle {
public void start ( Future startFuture ) throws Exception {
Future<Void> future = Future.future ();
HttpServer server = vertx.createHttpServer ();
Router router = Router.router (vertx);
router.get ("/sample").handler (this::sampeHandler);
router.get ("/testcompletableblocking").handler (this::testCompBlocking);
router.get ("/testcompletablenonblocking").handler (this::testCompNonBlocking);
server.requestHandler (router::accept) // <5>
.listen (8080, ar -> { // <6>
if (ar.succeeded ()) {
System.out.println ("Server started");
future.complete ();
} else {
System.out.println ("Server is not started");
future.fail (ar.cause ());
}
});
}
private void sampeHandler ( RoutingContext context ) {
try {
Thread.sleep (1000);
} catch (Exception e) {
}
String response = "Hello...";
context.response ().setStatusCode (200).putHeader ("content-type", "text/html").end (response);
}
private void testCompBlocking ( RoutingContext context ) {
System.out.println ("Calling testCompBlocking....");
HttpClientOptions clientOptions = new HttpClientOptions ().setDefaultHost ("localhost").setDefaultPort (8080).setSsl (false).setKeepAlive (true);
HttpClient client = vertx.createHttpClient (clientOptions);
String requestURI = "/sample";
CompletableFuture<String> comp = new CompletableFuture<> ();
HttpClientRequest request = client.request (HttpMethod.GET, requestURI, response -> {
response.bodyHandler (body -> {
String kmsResponse = new String (body.getBytes ());
System.out.println ("kmsResponse-" + kmsResponse);
comp.complete (kmsResponse);
});
}).exceptionHandler (e -> {
e.printStackTrace ();
comp.completeExceptionally (e);
});
request.end ();
String result = "Not Success";
try {
result = comp.get ();
} catch (Exception e) {
System.out.println ("Exception in getting from Completable..." + e.getMessage ());
e.printStackTrace ();
}
context.response ().setStatusCode (200);
context.response ().putHeader ("content-type", "text/html");
context.response ().end (result);
System.out.println ("end testCompBlocking....");
}
private void testCompNonBlocking ( RoutingContext context ) {
System.out.println ("Calling testCompNonBlocking....");
HttpClientOptions clientOptions = new HttpClientOptions ().setDefaultHost ("localhost").setDefaultPort (8080).setKeepAlive (false);
HttpClient client = vertx.createHttpClient (clientOptions);
String requestURI = "/sample";
CompletableFuture<String> comp = new CompletableFuture<> ();
HttpClientRequest request = client.request (HttpMethod.GET, requestURI, response -> {
response.bodyHandler (body -> {
String kmsResponse = new String (body.getBytes ());
System.out.println ("kmsResponse-" + kmsResponse);
comp.complete (kmsResponse);
});
}).exceptionHandler (e -> {
e.printStackTrace ();
comp.completeExceptionally (e);
});
request.end ();
String result = "Not Blocking, please see result at Console";
try {
comp.thenAccept (apiResult -> System.out.println ("apiResult from CompletableFuture - " + apiResult));
} catch (Exception e) {
System.out.println ("Exception in getting from Completable..." + e.getMessage ());
e.printStackTrace ();
}
context.response ().setStatusCode (200);
context.response ().putHeader ("content-type", "text/html");
context.response ().end (result);
System.out.println ("end testCompNonBlocking....");
}
}
Call localhost:8080/testcompletableblocking, response is not sent and current thread is blocked forever.
The problem with your implementation is that it violates The Golden Rule - Don’t Block the Event Loop. You should not call a blocking operation like CompletableFuture.get() on the event loop. Similarly, sampleHandler() should not call Thread.sleep() on the event loop either, but that's a lesser problem.
The consequence is that your event loop is now blocked… so your /sample request cannot be processed anymore. And since the request is not processed, you CompletableFuture remains uncompleted… deadlock.
There are two possible solutions to this problem:
Use CompletableFuture as designed, relying on chained calls instead of get(), though it does not enforce Vert.x's threading model. So for example:
comp.whenComplete((result, e) -> {
System.out.println("Got sample response");
if (e != null) {
context.response().setStatusCode(500)
.end(e.getMessage());
} else {
context.response().setStatusCode(200)
.putHeader("content-type", "text/html")
.end(result);
}
System.out.println("end testCompBlocking....");
});
Use Vert.x facilities for running blocking code. This shouln't be necessary with CompletableFuture but other API's might require it. So for example:
context.vertx().<String>executeBlocking(future -> {
String result = "Not Success";
try {
result = comp.get();
} catch (Exception e) {
System.out.println("Exception in getting from Completable..." + e.getMessage());
e.printStackTrace();
}
future.complete(result);
},
false,
result -> {
context.response().setStatusCode(200);
context.response().putHeader("content-type", "text/html");
context.response().end(result.result());
System.out.println("end testCompBlocking....");
});
get() blocks the main thread until the future completes, however, the HttpClientRequest is executed on the main thread so the situation results in a deadlock.
Conversely, thenAccept() is non-blocking and merely creates a callback that is executed when the future completes.
Your use-case isn't clear based on the code you've provided; Is there a reason that you are using HttpClient and CompletableFuture instead of WebClient and Future respectively?
If you need to use CompletableFuture then you should look into this project for a more Vert.x-compatible implementation.
Related
I have 3 methods that I need to run in parallel, since they are independent to each other and combine the results of each one at the end and send it as the response. I need to handle exception as well.
In different post I found the below code and modified accordingly.
public Response getResponse() {
Response resultClass = new Response();
try {
CompletableFuture<Optional<ClassA>> classAFuture
= CompletableFuture.supplyAsync(() -> service.getClassA() );
CompletableFuture<ClassB> classBFuture
= CompletableFuture.supplyAsync(() -> {
try {
return service.getClassB();
}
catch (Exception e) {
throw new CompletionException(e);
}
});
CompletableFuture<ClassC> classCFuture
= CompletableFuture.supplyAsync(() -> {
try {
return service.getClassC();
} catch (Exception e) {
throw new CompletionException(e);
}
});
CompletableFuture<Response> responseFuture =
CompletableFuture.allOf(classAFuture, classBFuture, classCFuture)
.thenApplyAsync(dummy -> {
if (classAFuture.join().isPresent() {
ClassA classA = classAFuture.join();
classA.setClassB(classBFuture.join());
classA.setClassC(classCFuture.join());
response.setClassA(classA)
}
return response;
});
responseFuture.join();
} catch (CompletionExecution e) {
throw e;
}
return response;
}
Should the above run correctly in parallel? I see it takes some more time, and I wanted to make sure I am doing it right.
If you want to run methods in parallel you should use ExecutorService. Try something like that:
ExecutorService myExecutor = Executors.newFixedThreadPool(3);
List<Future<Object>> futures = myExecutor.invokeAll(
Arrays.asList(
() -> service.getClassA(),
() -> service.getClassB(),
() -> service.getClassC(),
)
);
myExecutor.shutdown();
The idea is correct, but this all could be done with a lot less code:
public Response getResponse() {
CompletableFuture<Optional<ClassA>> classAFuture = CompletableFuture.supplyAsync(() -> service.getClassA());
CompletableFuture<ClassB> classBFuture = CompletableFuture.supplyAsync(() -> service.getClassB());
CompletableFuture<ClassC> classCFuture = CompletableFuture.supplyAsync(() -> service.getClassC());
try {
return CompletableFuture.allOf(classAFuture, classBFuture, classCFuture)
.thenApply(() -> {
Response response = new Response();
Optional<ClassA> maybeA = classAFuture.get();
if (maybeA.isPresent()) {
ClassA classA = maybeA.get();
classA.setClassB(classBFuture.get());
classA.setClassC(classCFuture.get());
response.setClassA(classA);
}
return response;
}).get();
} catch (ExecutionException e) { // Ususally the exception is wrapped to ExecutionException by java concurrency framework itself
Throwable cause = e.getCause();
if (cause != null) {
throw cause;
} else {
throw e;
}
}
}
Main things:
You don't need to wrap your exceptions to CompletionException.
You don't need to use thenApplyAsync. Just thenApply is the same thing unless you want to be very specific on the type of thread you want to use. Check this for more information https://stackoverflow.com/a/47489654/3020903
You don't need to join() anything. By the time CompletableFuture.all has finished, you can be very sure that all the supplied jobs have finished and calling get() on then will just return the value.
As for can you be sure jobs A, B and C will be run in parallel. Yes and no. It will be run in parallel if there are enough system resources to run them in parallel. You have done your best to ask them to run in parallel. Maybe at some point you also want to supply your custom thread pool to have more control, but that's a topic for another day.
Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 4 years ago.
Improve this question
I'm learning Java and I've a relatively simple Java program that fetches data from an API endpoint that looks like this:
public class Main {
public static String getJSON(String u) {
if (u == null) throw new IllegalArgumentException("URL is null.");
try {
URL url = new URL(u);
URLConnection site = url.openConnection();
InputStream is = site.getInputStream();
Scanner scanner = new Scanner(
new BufferedInputStream(is),
"UTF-8");
String resp = "";
while (scanner.hasNextLine()) {
resp = resp + scanner.nextLine();
}
return resp;
} catch (Exception e) {
System.out.println(e);
return null;
}
}
public static void main(String[] args) {
CompletableFuture<String> cf = CompletableFuture.supplyAsync(() ->
getJSON("https://jsonplaceholder.typicode.com/posts/1")
);
cf.thenAcceptAsync(System.out::println);
// System.out.println(cf.join()); <=== Commenting out this line
}
}
I expect the code above to print out the raw JSON, but instead, it does nothing. However, if I include the line that has been commented out above, the code works, but it prints out the raw JSON twice.
My guess is that the program terminates before the thenAcceptAsync has a chance to complete, which is not the case when the blocking .join() function is included. Is my guess right, and if so, how can I solve this issue?
Your main thread is not waiting for the completion of the service call. You should call join on the CompletableFuture to wait for its execution to finish:
cf.thenAcceptAsync(System.out::println).join();
You can check the behavior using the following modified version of your code (just adding a shutdown hook to print text when VM is exiting):
Runtime.getRuntime().addShutdownHook(new Thread(() -> System.out.println("Shutting down")));
CompletableFuture<String> cf = CompletableFuture.supplyAsync(() -> {
System.out.println("running...");
String result = getJSON("https://jsonplaceholder.typicode.com/posts/1");
System.out.println("Completed service call");
return result;
});
cf.thenAcceptAsync(System.out::println).join();
When you run the above code, the following is output:
running...
Completed service call
{ "result json here"}
Shutting down
However, without .join(), the following output appears immediately:
e
running...
Shutting down
In short, thenAcceptAsync(System.out::println) returns immediately and the main thread completes, in this case before the HTTP call completes. If you had work to do after that, it would like like:
cf = cf.thenAcceptAsync(System.out::println);
doSomethingElse();
doYetAnotherThing();
cf.join()
join should eventually be called, either to prevent premature termination of the VM, or to wait for the result to become ready when it's necessary.
import java.io.BufferedInputStream;
import java.io.InputStream;
import java.net.URL;
import java.net.URLConnection;
import java.util.Scanner;
import java.util.concurrent.CompletableFuture;
public class Main {
public static String getJSON(String u) {
if (u == null) throw new IllegalArgumentException("URL is null.");
try {
URL url = new URL(u);
URLConnection site = url.openConnection();
InputStream is = site.getInputStream();
Scanner scanner = new Scanner(
new BufferedInputStream(is),
"UTF-8");
String resp = "";
while (scanner.hasNextLine()) {
resp = resp + scanner.nextLine();
}
return resp;
} catch (Exception e) {
System.out.println(e);
return null;
}
}
public static void main(String[] args) {
CompletableFuture<String> cf = CompletableFuture.supplyAsync(() ->
getJSON("https://jsonplaceholder.typicode.com/posts/1")
);
//cf.thenAcceptAsync(System.out::println);
System.out.println(cf.join());
}
}
just comment and open below line it will print just one line
This question already has answers here:
How to asynchronously call a method in Java
(12 answers)
Closed 5 years ago.
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. **
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.
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)
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
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
}
I have been working with threads to send a GET request to a link (all good). However, I need it to run asynchronously, so I made a new thread and ran it. Problem is I need it to return the value returnVar[0] after the thread is done executing. I have tried while loops with !thread.isActive but of course, the method body needs a return statement. I have tried CountdownLatches which you are about to see, but they pause the main thread which I DON'T want. Any ideas are greatly appreciated.
Code:
public String getUUID(String username) {
final String[] returnVar = {"ERROR"};
final CountDownLatch latch = new CountDownLatch(1);
Thread thread = new Thread(() -> {
final String[] response = {"ERROR"};
final JSONObject[] obj = new JSONObject[1];
response[0] = ConnectionsManager.sendGet("https://api.mojang.com/users/profiles/minecraft/" + username);
try {
obj[0] = (JSONObject) new JSONParser().parse(response[0]);
returnVar[0] = (String) obj[0].get("id");
} catch (ParseException e) {
e.printStackTrace();
}
latch.countDown();
});
thread.start();
try {
latch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
return returnVar[0];
}
I think you should consider using a Callable instead of a Runnable. See this thread for explanation and examples.
Also, it's a little strange that you are using the CountDownLatch with one thread. The latch is useful to make sure multiple threads are started as uniformly as possible rather than some threads getting a 'head start' in a more traditional startup.
this is an improper use of Threads.
your code runs exactly like the below code :
public String getUUID(String username) {
String response = ConnectionsManager.sendGet("https://api.mojang.com/users/profiles/minecraft/" + username);
try {
return (String) ((JSONObject) new JSONParser().parse(response)).get("id");
} catch (ParseException e) {
return "ERROR";
}
}
there are several options to make async call.
one option is to use CompletableFuture :
CompletableFuture.supplyAsync(getUUID("username")).thenAccept(new Consumer<String>() {
#Override
public void accept(String response) {
// response of async HTTP GET
}
});
learn more :
http://www.javaworld.com/article/2078809/java-concurrency/java-concurrency-java-101-the-next-generation-java-concurrency-without-the-pain-part-1.html
http://javarevisited.blogspot.nl/2015/01/how-to-use-future-and-futuretask-in-Java.html
Difference between Future and Promise
https://www.javacodegeeks.com/2011/09/java-concurrency-tutorial-callable.html
http://winterbe.com/posts/2015/04/07/java8-concurrency-tutorial-thread-executor-examples/
http://www.infoq.com/articles/Functional-Style-Callbacks-Using-CompletableFuture
I am trying to signal between two threads using the below FutureResult class which extends FutureTask class. When run the script, it prints the following result.
SENDING: 0
SENT: 0
POLL: FutureResult#513431
SIGNALLED: FutureResult#513431
Then the program hang up forever. I expect FutureResult instance should return the value from it's blocking get method. Then print the result in the console. But FutureResult.get is blocking forever.
import java.util.concurrent.*;
/**
* Created by someone on 20/08/2015.
*/
final public class FutureResult<T> extends FutureTask<T> {
private static final Object SS = "SS";
public FutureResult() {
super(() -> null);
}
public void signal(final T value) {
set(value);
}
public void signalError(final Throwable throwable) {
setException(throwable);
}
public static void main(String... args) throws Exception {
final ArrayBlockingQueue<FutureResult> queue = new ArrayBlockingQueue<>(1000000);
new Thread(() -> {
while (true) {
try {
final FutureResult poll = queue.take();
System.out.println("POLL: " + poll);
if (poll != null) {
poll.signal(SS);
System.out.println("SIGNALLED: " + poll);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}).start();
new Thread(() -> {
for (int i = 0; i < 1; i++) {
final FutureResult<Object> result = new FutureResult<>();
System.out.println("SENDING: " + i);
queue.offer(new FutureResult());
try {
System.out.println("SENT: " + i);
result.get();
System.out.println("GOT : " + i);
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
}).start();
}
}
This is the problem:
queue.offer(new FutureResult());
You're setting the value on one FutureResult, but that's not the one you're waiting for. Just change that line to:
queue.offer(result);
and it works fine.
Looks like the confusion is in the use of FutureTask. FutureTask is designed as a Runnable; running it is necessary.
Honestly, based on the code, it looks like the custom code is implementing something similar to FutureTask. If the intent here is to learn to use FutureTask, then create a FutureTask instance with a "run" method, and then execute that run method. On completion of the run method, the FutureTask.get() will complete.