4

I'm trying to understand if there are any visibility-guarantees provided by CompletableFuture.

Suppose I've a class called SampleClass which is something like the following:

public class SampleClass {
    private String member1;
    private String member2;
    // getters, setters and constructor;
}

And I do something like this:

SampleClass sampleClass = new SampleClass();

CompletableFuture<Void> cf1 = CompletableFuture.supplyAsync(() -> "Hello")
        .thenAccept(sampleClass::setMember1);

CompletableFuture<Void> cf2 = CompletableFuture.supplyAsync(() -> " World")
        .thenAccept(sampleClass::setMember2);

cf1.join();
cf2.join();

// sout(sampleClass);

Now, I want to understand that in the sout statement, can there be a case when one or both the members is/are not initialized?

Basically is there any visibility-guarantee that's provided by CompletableFuture in here? (I'm under the impression that there's no such guarantee provided by CompletableFuture and the above code is broken.)

I've already gone through this question but I think it's not what I need.

Lavish Kothari
  • 2,211
  • 21
  • 29
  • 5
    `join()` is a convenience alternative to `get()`. And `Future.get()` has been addressed in [the package documentation](https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/package-summary.html#MemoryVisibility). It would be nice, for sure, if they expanded this part for Java 8 features. But I still consider its current form sufficient for assuming the right memory visibility. With one caveat, there must be no other completion. Since anyone can call `complete` on a `CompletableFuture`, such call would void any guarantees about any completion, as you don’t know which won. – Holger May 04 '20 at 16:03
  • Thanks @Holger for the comment. I got what you're hinting at. So `Future.get()` ensures *happen-before* any action subsequent to `Future.get()`, so the call to `cf2.join()` should see the updated value of `sampleClass`. Is my understanding correct? And this visibility-guarantee won't be there if from somewhere, we're calling `CompletableFuture.complete()`, right? – Lavish Kothari May 04 '20 at 16:55
  • 4
    Exactly. In case of multiple completion attempts, there will still be a *happens-before* relationship between the completion which “won” and the actions after `join()`, however, you’d need a safe way to determine which completion it was. For the typical case of evaluating functions which produce a result, it is safe to use the value returned by `join()`, as it is the result of the successful completion. But this doesn’t work for a side-effect like `setMember2`. The same applies to `CompletableFuture.anyOf(c1, c2).join()`; it would not give you any guarantees. – Holger May 05 '20 at 06:49
  • @Holger thanks a lot for this great explanation! This was really helpful. – Lavish Kothari May 07 '20 at 13:56

0 Answers0