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 notfinal
.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 inFoo
, the invocation ofprintHi()
, which does not read the object’s state, bears no potential data race. Note that this only applies to exact instances ofFoo
, produced bynew Foo(…)
expressions, as subclasses could overrideprintHi()
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 sharedstatic
variable of a different class, it could produce a data race, even if theFoo
instance is immutable and/or properly published. The code invokingfoo.printHi()
in another thread is safe, ifprintHi()
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 ofFoo
(assuming theFoo
instance is not modified afterwards).