Understanding behaviour of Java's Completable Future [closed] - java

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

Related

Vertx HTTPClient with CompletableFuture block the callback Thread

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.

Java Future: Issue while trying to understand .get() method

import java.io.*;
import java.util.concurrent.*;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.Callable;
class _TimeOut_ extends PrintIn_Delays {
public static void main(String[] args)
throws InterruptedException {
TimeWait Timeout = new TimeWait();
String input = Timeout.readLine();
String input2 = Timeout.readLine();
}
}
class Reader implements Callable<String> {
public String call() throws IOException {
BufferedReader br = new BufferedReader(
new InputStreamReader(System.in));
String input;
do {
input = br.readLine();
}while ("".equals(input));
return input;
}
}
class TimeWait extends _TimeOut_ {
public String readLine() throws InterruptedException {
ExecutorService ex = Executors.newSingleThreadExecutor();
String input = null;
try {
try {
Future<String> result = ex.submit(
new Reader());
input = result.get(5, TimeUnit.SECONDS);
} catch (ExecutionException e) {
e.getCause().printStackTrace();
} catch (TimeoutException e){}
} finally {
ex.shutdownNow();
}
System.out.println(" "+input);
return input;
}
}
This will wait for 5 seconds for user input. If user don't enter anything, it displays null.
Now the problem is :
When I run it, it waits for 5 seconds for my input but I don't enter anything and so output is null. Then in the second input, I enter 'hi'. But it still waits for 5 seconds ( which it shouldn't) and after taking the input, it still displays null. Here is the output :
null
hi
null
The problem is that even though you are stopping waiting for the first Future to get a value, it doesn't stop waiting for a value. The BufferedReader.readLine() call simply blocks, waiting for input, or closure of the input stream, irrespective of the timeout.
So, when you do eventually enter a value, the first thread gets it; then there is nothing for the second thread to read.
(But, of course, the second thread hasn't stopped waiting either: this will continue to wait for input, or closure of the input stream).

Calling two Java methods asynchronously [duplicate]

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
}

Asynchronously running a task and returning after thread is active

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

To check if a file is written completely

How do I know if a software is done writing a file if I am executing that software from java?For example, I am executing geniatagger.exe with an input file RawText that will produce an output file TAGGEDTEXT.txt. When geniatagger.exe is finished writing the TAGGEDTEXT.txt file, I can do some other staffs with this file. The problem is- how can I know that geniatagger is finished writing the text file?
try{
Runtime rt = Runtime.getRuntime();
Process p = rt.exec("geniatagger.exe -i "+ RawText+ " -o TAGGEDTEXT.txt");
}
You can't, or at least not reliably.
In this particular case your best bet is to watch the Process complete.
You get the process' return code as a bonus, this could tell you if an error occurred.
If you are actually talking about this GENIA tagger, below is a practical example which demonstrates various topics (see explanation about numbered comments beneath the code). The code was tested with v1.0 for Linux and demonstrates how to safely run a process which expects both input and output stream piping to work correctly.
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.util.concurrent.Callable;
import org.apache.commons.io.IOUtils;
public class GeniaTagger {
/**
* #param args
*/
public static void main(String[] args) {
tagFile(new File("inputText.txt"), new File("outputText.txt"));
}
public static void tagFile(File input, File output) {
FileInputStream ifs = null;
FileOutputStream ofs = null;
try {
ifs = new FileInputStream(input);
ofs = new FileOutputStream(output);
final FileInputStream ifsRef = ifs;
final FileOutputStream ofsRef = ofs;
// {1}
ProcessBuilder pb = new ProcessBuilder("geniatagger.exe");
final Process pr = pb.start();
// {2}
runInThread(new Callable<Void>() {
public Void call() throws Exception {
IOUtils.copy(ifsRef, pr.getOutputStream());
IOUtils.closeQuietly(pr.getOutputStream()); // {3}
return null;
}
});
runInThread(new Callable<Void>() {
public Void call() throws Exception {
IOUtils.copy(pr.getInputStream(), ofsRef); // {4}
return null;
}
});
runInThread(new Callable<Void>() {
public Void call() throws Exception {
IOUtils.copy(pr.getErrorStream(), System.err);
return null;
}
});
// {5}
pr.waitFor();
// output file is written at this point.
} catch (Exception e) {
e.printStackTrace();
} finally {
// {6}
IOUtils.closeQuietly(ifs);
IOUtils.closeQuietly(ofs);
}
}
public static void runInThread(final Callable<?> c) {
new Thread() {
public void run() {
try {
c.call();
} catch (Exception e) {
e.printStackTrace();
} finally {
}
}
}.start();
}
}
Use a ProcessBuilder to start your process, it has a better interface than plain-old Runtime.getRuntime().exec(...).
Set up stream piping in different threads, otherwhise the waitFor() call in ({5}) might never complete.
Note that I piped a FileInputStream to the process. According to the afore-mentioned GENIA page, this command expects actual input instead of a -i parameter. The OutputStream which connects to the process must be closed, otherwhise the program will keep running!
Copy the result of the process to a FileOutputStream, the result file your are waiting for.
Let the main thread wait until the process completes.
Clean up all streams.
If the program exits after generating the output file then you can call Process.waitFor() to let it run to completion then you can process the file. Note that you will likely have to drain both the standard output and error streams (at least on Windows) for the process to finish.
[Edit]
Here is an example, untested and likely fraught with problems:
// ...
Process p = rt.exec("geniatagger.exe -i "+ RawText+ " -o TAGGEDTEXT.txt");
drain(p.getInputStream());
drain(p.getErrorStream());
int exitCode = p.waitFor();
// Now you should be able to process the output file.
}
private static void drain(InputStream in) throws IOException {
while (in.read() != -1);
}

Categories