Say I have a class
public class Foo {
public void printHi() {
System.out.print("Hi");
}
}
and in some client code I do something like
public static void main() {
Foo foo = new Foo();
(new Thread(() -> {foo.printHi();})).start();
}
and take away the happens-before guarantee for calling Thread Start. Then is it possible that the Foo reference might not be visible to that thread using it or worse, the method that belongs to that class is not visible, but the Foo reference is visible. I am not sure how method is stored in an object like fields, but this assumes that it is just something in memory belonging to that object, so it might have visibility issues, but that I am not sure of. Can someone also explain that part to me?
I ask this because Foo is immutable, and in the JCIP book Goetz says that
"Immutable objects, on the other hand, can be safely accessed even when synchronization is not used to publish the object reference. For this guarantee of initialization safety to hold, all of the requirements for immutability must be met: unmodi-fiable state, all fields are final, and proper construction" (Goetz, 3.5.2)
However, it doesn't have any final fields, so does it count as if all fields are final? Since no fields = all fields?
There are different ways to get to the same answer.
Your object is immutable, as it bears no state that can be modified.
All of its fields are final
, as there is no field that is not final
.
There is no possible race condition, as there are no data that could be modified while being accessed.
Even if there were some non-final
fields declared in Foo
, the invocation of printHi()
, which does not read the object’s state, bears no potential data race. Note that this only applies to exact instances of Foo
, produced by new Foo(…)
expressions, as subclasses could override printHi()
to access shared mutable state.
It’s important to emphasize that race condition are about the shared mutable data, not necessarily objects. So if printHi()
accesses a shared static
variable of a different class, it could produce a data race, even if the Foo
instance is immutable and/or properly published. The code invoking foo.printHi()
in another thread is safe, if printHi()
does not access shared mutable state (or only using proper guarding).
As Elliott Frisch mentioned, a lambda expression behaves like an immutable object anyway, so the code would be safe even without the happens-before relationship of Thread.start()
or the immutability of Foo
(assuming the Foo
instance is not modified afterwards).