This is specified in JLS, §17.6. Word Tearing:
One consideration for implementations of the Java Virtual Machine is that every field and array element is considered distinct; updates to one field or element must not interact with reads or updates of any other field or element.
So the fact that a might written by a different thread than b in your example, does not create any data race.
But it still requires a thread safe mechanism to read the result. In your example, it’s the parallel stream which guarantees that the initiating thread can safely read the two variables after forEach returned.
You example can be simplified to
public MyObject compute() { MyObject newObj = new MyObject(); Stream.<Runnable>of(() -> newObj.setA(computeA()), () -> newObj.setB(computeB())) .parallel().forEach(Runnable::run); return newObj; }
But the recommended pattern would be to execute the calculation first, followed by constructing the object, which can be designed as immutable object then.
public class MyObject { public final Integer a, b; public MyObject(Integer a, Integer b) { this.a = a; this.b = b; } }
public MyObject compute() { return CompletableFuture.supplyAsync(() -> computeA()) .thenCombine(CompletableFuture.supplyAsync(() -> computeB()), MyObject::new) .join(); }
This way, you can be sure that any thread seeing the MyObject will see consistent values for the fields, regardless of what happens in the remaining application.
forEachreturned. And there’s no other access to these variable prior to the completion.forEach()after the operation, but e.g. updating a thread-unsafe collection isn't safe due to the concurrent operation. I very much appreciate your continuing insight on concurrency and other issues on SO.