Home > Software design >  final List<T> and thread safety
final List<T> and thread safety

Time:11-15

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.

  1. You are publishing (and potentially mutating) the state of Dummy before its constructor has completed. This is unsafe whether or not the variable is final.

  2. 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 again final 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.

  • Related