CompletableFuture.thenAccept can indeed block - java

Unlike stated in some blogs(e.g. I can't emphasize this enough: thenAccept()/thenRun() methods do not block) CompletableFuture.thenAccept can indeed block. Consider the following code, uncommenting the pause method call will cause thenAccept to block:
CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
log.trace("return 42");
return "42";
});
//pause(1000); //uncommenting this will cause blocking of thenAccept
future.thenAccept((dbl -> {
log.trace("blocking");
pause(500);
log.debug("Result: " + dbl);
}));
log.trace("end");
pause(1000);
Can we be sure that the following will not block? It's my understanding that if the supplyAsync runs immediately then the thenAccept could block, no?
CompletableFuture<Void> future = CompletableFuture.supplyAsync(() -> {
return "42";
}).thenAccept((dbl -> {
pause(500);
log.debug("Result: " + dbl);
}));

You are right, thenAccept() will block if the future is already completed. Also note that when it is not the case, it will cause the thread that completes it to block at the time of completion.
This is why you have thenAcceptAsync(), which will run your Consumer in a non-blocking way:
CompletableFuture<Void> future = CompletableFuture.supplyAsync(() -> {
return "42";
}).thenAcceptAsync((dbl -> {
pause(500);
log.debug("Result: " + dbl);
}));
See also Which executor is used when composing Java CompletableFutures?

Related

Completablefuture doesnot complete on exception

I'm kinda new to using CompletableFuture API and I have a question regarding usage of allOf. From what I read, completable-future should be in complete state and allOf logic should be executed when all associated futures complete, including completed-exceptionally. But here's my smaple code for which allOf block never gets executed -
public static void test() {
CompletableFuture<String> r1 = CompletableFuture.supplyAsync(() -> {
try{
Thread.sleep(1000);
throw new RuntimeException("blahh !!!");
}catch (Exception e) {
throw new RuntimeException(e);
}
});
CompletableFuture<String> r2 = CompletableFuture.supplyAsync(() -> "55");
CompletableFuture<String> r3 = CompletableFuture.supplyAsync(() -> "56");
CompletableFuture.allOf(r1, r2, r3).thenRun(() -> { System.out.println(Thread.currentThread()+" --- End."); });
Stream.of(r1, r2, r3).forEach(System.out::println);
try{
System.out.println(Thread.currentThread()+" --- SLEEPING !!!");
Thread.sleep(3000);
System.out.println(Thread.currentThread()+" --- DONE !!!");
} catch (Exception e) {
//e.printStackTrace();
}
Stream.of(r1, r2, r3).forEach(System.out::println);
}
The problem is not that your allOf CompletableFuture never completes. It does.
What causes your code not to run is thenRun's expectation:
Returns a new CompletionStage that, when this stage completes normally, executes the given action. See the CompletionStage documentation for rules covering exceptional completion.
You probably already know that when one of allOf's futures completes exceptionally, the resulting future also completes exceptionally:
Returns a new CompletableFuture that is completed when all of the given CompletableFutures complete. If any of the given CompletableFutures complete exceptionally, then the returned CompletableFuture also does so, with a CompletionException holding this exception as its cause.
In short, don't use thenRun if you want to run an action on your allOf future irrespective of how it comples. As an alternative, you can use whenComplete:
CompletableFuture.allOf(r1, r2, r3)
.whenComplete((a, ex) -> System.out.println(Thread.currentThread() + " --- End."));
You can also use a combination of thenRun + exceptionally, one of which will run:
CompletableFuture<Void> all = CompletableFuture.allOf(r1, r2, r3);
all.thenRun(() -> {
System.out.println(Thread.currentThread() + " --- End.");
});
all.exceptionally(ex -> {
System.out.println(ex);
return null;
});

Why CompletableFuture did not execute in sequence?

My goal is to understand how CompletableFuture works.
My expected result: If I do CompletableFuture.runAsync().thenRun().thenRunAsync(). The thread will be executed in sequence runAsync() -> thenRun() -> thenRunAsync().
My actual result: The sequence is race condition. Sometimes:
runAsync -> thenRunAsync+e -> ...
runAsync -> thenRun -> ...
Reference from SO
public class RunExample5 {
public static void main(String[] args) {
ExecutorService e = Executors.newSingleThreadExecutor(r -> new Thread(r, "sole thread"));
CompletableFuture<?> f = CompletableFuture.runAsync(() -> {
System.out.println("runAsync:\t" + Thread.currentThread());
LockSupport.parkNanos((int) 1e9);
}, e);
f.thenRun(() -> System.out.println("thenRun:\t" + Thread.currentThread()));
f.thenRunAsync(() -> System.out.println("thenRunAsync:\t" + Thread.currentThread()));
f.thenRunAsync(() -> System.out.println("thenRunAsync+e:\t" + Thread.currentThread()),
e);
LockSupport.parkNanos((int) 2e9);
e.shutdown();
}
}
You need
f.thenRun(() -> System.out.println("thenRun:\t" + Thread.currentThread()))
.thenRunAsync(() -> System.out.println("thenRunAsync:\t" + Thread.currentThread()))
.thenRunAsync(() -> System.out.println("thenRunAsync+e:\t" + Thread.currentThread()), e);
The interface to CompletableFuture doesn't work quite the way you're imagining. f itself doesn't keep track of every call to thenRun or thenRunAsync and run them in order; instead, it treats everything from thenRun or thenRunAsync as simultaneously runnable as soon as the main work completes. If you want to chain more complicated sequences of work, you need to use the return value of thenRun or thenRunAsync -- a CompletionStage object -- and call thenRunAsync on that.

will CompletableFuture callback always be executed

I have two service calls:
String call1() { ... return "ok"; }
void call2(String) { ... }
I know the basic way for CompletableFuture with callback is like
CompletableFuture<Void> future = CompletableFuture
.supplyAsync(() -> call1())
.thenAccept(s -> call2(s));
future.join();
What if I separate the two chained CompletableFutures, like:
CompletableFuture<String> future1 = CompletableFuture.supplyAsync(() -> call1());
CompletableFuture<Void> future2 = future1.thenAccept(s -> call2(s));
future1.join(); // will call2 be executed at this time?
Is this any different from calling join() on future2:
CompletableFuture<String> future1 = CompletableFuture.supplyAsync(() -> call1());
CompletableFuture<Void> future2 = future1.thenAccept(s -> call2(s));
future2.join();
What if I call join() on both of the futures?
CompletableFuture<String> future1 = CompletableFuture.supplyAsync(() -> call1());
CompletableFuture<Void> future2 = future1.thenAccept(s -> call2(s));
future1.join();
future2.join();
It seems they are all the same from running my sample code. But I feel something might be wrong somewhere. Thanks!
They are not the same.
In short, you can look at it as future1 and future2 hold results of distinct tasks (even if future2 uses the result of future1, it's a different future).
future1.join() will block until () -> call1() ends, and future2's task won't start until then. future2.join() will wait until s -> call2(s) is done.
What if I separate the two chained CompletableFutures, like:
This makes no difference as far as task execution is concerned. It's either a question of style or it only matters when you need to use the two future objects separately.
What if I call join() on both of the futures?
It's redundant in this case to call future1.join() as you are not doing anything between the two .join calls. It would make sense if you wanted to perform some action "after completion of task1 and before the completion of task 2".
In this case, though, calling future2.join() is enough.
And the code snippet below should show how this behaves:
public static void main(String[] args) {
CompletableFuture<Void> future1 = CompletableFuture.runAsync(() -> delay());
CompletableFuture<Void> future2 = future1.thenRun(() -> delay());
long start = System.currentTimeMillis();
future1.join();
System.out.println("Future 1 done. Waiting for future 2: "
+ (System.currentTimeMillis() - start));
future2.join();
System.out.println("Future 2 complete: "
+ (System.currentTimeMillis() - start));
}
static void delay() {
try {
TimeUnit.SECONDS.sleep(5);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
As it is, this code outputs:
Future 1 done. Waiting for future 2: 5001
Future 2 complete: 10001
But when you remove future1.join(), the output becomes:
Future 2 complete: 10001
Which simply means that future1.join() is superfluous unless you have actions to perform between the completions of the two futures
The supplyAsync and thenAccept methods should be executed on a separate thread automatically according to the documentation:
All async methods without an explicit Executor argument are performed using the ForkJoinPool.commonPool()
In your examples, the only difference is your thread waits on different events before continuing due to the joining. Here is the breakdown:
CompletableFuture<Void> future = CompletableFuture
.supplyAsync(() -> call1())
.thenAccept(s -> call2(s));
future.join();
This will wait until call2 completes because that is the future returned by thenAccept method.
CompletableFuture<String> future1 = CompletableFuture.supplyAsync(() -> call1());
CompletableFuture<Void> future2 = future1.thenAccept(s -> call2(s));
future1.join(); // will call2 be executed at this time?
This will only wait until call1 completes and moves on. call2 will still gets executed.
CompletableFuture<String> future1 = CompletableFuture.supplyAsync(() -> call1());
CompletableFuture<Void> future2 = future1.thenAccept(s -> call2(s));
future2.join();
This is identical to the first one.
CompletableFuture<String> future1 = CompletableFuture.supplyAsync(() -> call1());
CompletableFuture<Void> future2 = future1.thenAccept(s -> call2(s));
future1.join();
future2.join();
Calling future1.join() ends when call1 completed, then future2.join() ends when call2 completed. This should be identical functionally with the first one as well.

How to interrupt CompletableFuture::join?

I've found that CompletableFuture::join seems uninterruptible when not completed:
// CompletableFuture::join implementation from JDK 8 sources
public T join() {
Object r;
return reportJoin((r = result) == null ? waitingGet(false) : r);
}
In above implementation, waitingGet(false) will ignore the interrupt flag of the working Thread and continue waiting. I'm wondering how can I interrupt a Thread in which I call CompletableFuture::join.
Do not use join() if you want to support interruption, use get() instead. Basically they are the same except:
join() is only defined in CompletableFuture whereas get() comes form interface Future
join() wraps exceptions in CompletionException whereas get() wraps them in ExecutionException
get() might be interrupted and would then throw an InterruptedException
Note that what you interrupt is the Thread, not the Future. For example, the following code interrupts the main thread while it is waiting on myFuture.get():
CompletableFuture<Void> myFuture = new CompletableFuture<>();
Thread mainThread = Thread.currentThread();
CompletableFuture.runAsync(() -> {
try {
Thread.sleep(1000);
System.out.println("Interrupting…");
mainThread.interrupt();
Thread.sleep(1000);
System.out.println("Completing");
myFuture.complete(null);
} catch (InterruptedException e) {
e.printStackTrace();
}
});
try {
myFuture.get();
System.out.println("Get succeeded");
} catch (Exception e) {
System.out.println("Get failed");
e.printStackTrace();
}
Output:
Interrupting…
Get failed
java.lang.InterruptedException
at java.util.concurrent.CompletableFuture.reportGet(CompletableFuture.java:347)
at java.util.concurrent.CompletableFuture.get(CompletableFuture.java:1895)
at CompletableFutureInteruption.main(CompletableFutureInteruption.java:37)
…
If you replace get() by join(), the interrupt will indeed not work.
I finally give up to interrupt the Thread which blocks in waiting for CompletableFuture::join finish.
Instead I use CompletableFuture::allof to get a CompletableFuture all which ends when all my joined Futures end. And then just call the get() method of the all Future in the working thread. When get() returns, I then collect all my results by iterating all my joined Futures and call getNow on them. Such a procedure is interruptible.
I clearly known in the end you are not actually looking for the interruptible, but as a way around it can be interrupted by exceptions as follows (though it's join()):
private static void testCompleteExceptionally() {
String name = "Hearen";
CompletableFuture<String> completableFuture
= CompletableFuture.supplyAsync(() -> {
delay(500L);
if (name == null) {
throw new RuntimeException("Computation error!");
}
return "Hello, " + name;
});
if (name != null) {
completableFuture.completeExceptionally(new RuntimeException("Calculation failed!"));
}
out.println(completableFuture.handle((s, t) -> s != null ? s : "Hello, Stranger!" + t.toString()).join());
}

Delegating to threads while preserving linear readability

I've been experimenting with different ways to handle blocking methods with disconnected results while maintaining state which might have been interrupted. I've found it to be frustrating having to deal with disparate classes and methods where sending and receiving are difficult to align.
In the following example, SomeBlockingMethod() normally returns void as a message is sent to some other process. But instead I've made it synchronized with a listener which receives the result. By spinning it off to a thread, I can wait() for the result with a timeout or indefinitely.
This is nice because once the result is returned, I can continue working with a particular state which I had to pause while waiting for the result of the threaded task.
This there anything wrong with my approach?
Although this question may seem generic, I am specifically looking for advice on threading in Java.
Example pseudocode:
public class SomeClass implements Command {
#Override
public void onCommand() {
Object stateObject = new SomeObjectWithState();
// Do things with stateObject
Runnable rasync = () -> {
Object r = SomeBlockingMethod();
// Blocking method timed out
if (r == null)
return;
Runnable rsync = () -> {
// Continue operation on r which must be done synchronously
// Also do things with stateObject
};
Scheduler().run(rsync);
};
Scheduler().run(rasync);
}
Update with CompletableFuture:
CompletableFuture<Object> f = CompletableFuture.supplyAsync(() -> {
return SomeBlockingMethod();
});
f.thenRun(() -> { () -> {
String r = null;
try {
r = f.get();
}
catch (Exception e) {
e.printStackTrace();
}
// Continue but done asynchronously
});
or better yet:
CompletableFuture.supplyAsync(() -> {
return SomeBlockingMethod();
}).thenAccept((
Object r) -> {
// Continue but done asynchronously
});
The problem with using strictly CompletableFuture is that CompletableFuture.thenAccept is run from the global thread pool and is not guaranteed to be synchronous with the calling thread.
Adding the scheduler back for the synchronous task fixes this:
CompletableFuture.supplyAsync(() -> {
return SomeBlockingMethod();
}).thenAccept((
Object r) -> {
Runnable rsync = () -> {
// Continue operation on r which must be done synchronously
};
Scheduler().run(rsync);
});
A caveat of using CompletableFuture compared to the complete scheduler method is that any previous state which exists outside must be final or effectively final.
You should check out RxJava, it uses stream manipulation and has threading support.
api.getPeople()
.observeOn(Schedulers.computation())
.filter(p -> return p.isEmployee();)
.map(p -> return String.format("%s %s - %s", p.firstName(), p.lastName(), p.payrollNumber());)
.toList()
.observerOn(<ui scheudler>)
.subscirbe(p -> screen.setEmployees(p);)

Categories

Resources