I'm trying to understand how does final
field acts in multi-threaded environment.
I read these related posts:
final fields and thread-safety
Java concurrency: is final field (initialized in constructor) thread-safe?
Java: Final field freeze on object reachable from final fields
and tried to simulate situation where final
field will prevent threads from working with not fully constructed object.
public class Main {
public static void main(String[] args) {
new _Thread().start();
new Dummy();
}
static class _Thread extends Thread {
static Dummy dummy;
@Override
public void run() {
System.out.println(dummy.getIntegers().size() == 10_000);
}
}
static class Dummy {
private final List<Integer> integers = new ArrayList<>();
public Dummy() {
_Thread.dummy = this;
for (int a = 0; a < 10_000; a ) {
integers.add(a);
}
}
public List<Integer> getIntegers() {
return integers;
}
}
}
So as I understood, _Thread
will stop execution on getIntegers()
and wait until loop finish filling collection. But whether there is a final
modifier or not on field integers
, result of run()
is unpredictable. Also I know that there is a possibility of NPE.
CodePudding user response:
The final
makes no difference here. The code is not thread-safe, whether the final
is there or not.
There are two reasons that this is not thread-safe.
You are publishing (and potentially mutating) the state of
Dummy
before its constructor has completed. This is unsafe whether or not the variable isfinal
.You are returning a shared mutable object in the
getIntegers()
call. So that means that the caller could change it, and a second caller may or may not see the results ... due to lack of synchronization. Once againfinal
makes no difference to this.
The thread-safety guarantees of final
are limited. Here's what the JLS says:
final
fields also allow programmers to implement thread-safe immutable objects without synchronization. A thread-safe immutable object is seen as immutable by all threads, even if a data race is used to pass references to the immutable object between threads. This can provide safety guarantees against misuse of an immutable class by incorrect or malicious code.final
fields must be used correctly to provide a guarantee of immutability.An object is considered to be completely initialized when its constructor finishes. A thread that can only see a reference to an object after that object has been completely initialized is guaranteed to see the correctly initialized values for that object's
final
fields.
The takeaways are that the final
guarantees only apply to immutable objects, and they only apply after the return of the object's constructor.
In your example does not satisfy either of these prerequisites. Therefore, the guarantees do not apply.
CodePudding user response:
The final
keyword does not make the list, or its contents immutable/thread-safe. What the final
keyword does is prevent you from assigning a new value to the integers
variable. For instance, you cannot write:
final List<Integer> integers = new ArrayList<>();
integers = createNewList();
Therefore, multiple threads can add, remove or fetch elements from the list concurrently. This is why the result of run()
is unpredictable.