0

I would like to understand why CompletableFuture is thread-safe in this example. Let's say I have this class:

public class A {
  private Integer someInt = 3;

  public A(final Integer someInt) {
    this.someInt = someInt;
  }

  ... setter and getter

}

and a CompletableFuture that will return an instance of A.

Then I have one thread that called get() and is waiting for the result. There is also another thread that will supply the result to the CompletableFuture by calling complete(new A(5)).

If you check the code for complete(T), it will call completeValue(T) which in turn will use UNSAFE.compareAndSwapObject(...) to set the result. If I'm not mistaken this will only publish the reference to instance A but it won't publish the reference to someInt inside A, so the thread that is waiting might not have the correct result.

Tiago Martins
  • 165
  • 1
  • 1
  • 11
  • Your question here is not really about `CompletableFuture`, it is about safely using an instance of `A` in multiple threads. You are right that [an uninitialized `A` could leak](https://stackoverflow.com/questions/16178020/uninitialized-object-leaked-to-another-thread-despite-no-code-explicitly-leaking). But that problem is separate from this mechanism and would be true of any mechanism that shares the `A`. Please see [this answer](https://stackoverflow.com/a/7145024/666583) regarding techniques to ensure the A is fully initialized. – AndrewF Mar 26 '21 at 02:23
  • The problem that I'm talking about doesn't happen just with initialization. Let's pretend that the object was initilized and published correcly to all threads. Then if you change the value of `someInt` with a setter in a thread and call `complete(...)`, the same problem would happen. (Maybe this is not a problem and `CompletableFuture` assures that the other threads will have visibility to `someInt`, I just would like to understand how that is achieved, because looking at the docs and `CompletableFuture` code I can't find it. – Tiago Martins Mar 26 '21 at 02:58
  • 1
    I have thought about this more, and I understand your question better now. The call to `UNSAFE.compareAndSwapObject` is working with a volatile field, which is enough to _transitively_ guarantee a happens-before relationship here. Within thread A, the constructor is guaranteed to happen before the volatile set, which is guaranteed to happen before thread B's read. This is discussed heavily in many answers, but [here is one of the more clear ones](https://stackoverflow.com/a/39393985/666583). Regarding publishing non-constructor changes within `A`, that safety is `A`'s job (`volatile` field). – AndrewF Mar 26 '21 at 03:14
  • 1
    While @AndrewF's reasoning about application of happens-before relationship to this case is correct up to my knowledge, to avoid any doubts and make the reasoning about thread safety simpler in similar cases, it is better to use `CompletableFuture` with immutable objects (i.e. declare `someInt` as final and remove the setter for it). – Vladimir Korenev Mar 26 '21 at 07:03
  • `CompletableFuture` does _not_ make reads and writes to A.someInt thread-safe. The completable future does not even know that `someInt` exists; all it cares about is its reference (the "pointer") to `A`. What's behind this `A` is irrelevant to the completable future instance. – knittl Jan 12 '23 at 19:18

0 Answers0