0

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.

lkatiforis
  • 5,703
  • 2
  • 16
  • 35
zexed640
  • 167
  • 1
  • 6
  • 2
    What are you actually asking? As far as I can see, this "question" doesn't actually contain any questions. Just a statement of intent, and some assertions about how you believe this code should behave. What do you actually want us to answer here? – Stephen C Nov 14 '21 at 04:11
  • 1
    IMO, you are mixing up what "fully constructed" means. The `ArrayList` instance is _empty_ when it is "fully constructed." Then, the `Dummy` constructor _mutates_ the list, _after_ the list has been "constructed." Your use of `final` prevents the other thread from seeing the _list_ in an un-constructed state, but you allow the other thread to see the `Dummy` instance before the `Dummy` is fully constructed. You allow it to see the `Dummy` while its constructor still is mutating the list. – Solomon Slow Nov 14 '21 at 14:01

1 Answers1

2

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.

Stephen C
  • 698,415
  • 94
  • 811
  • 1,216
  • As for example int this question - https://stackoverflow.com/questions/6457109/java-concurrency-is-final-field-initialized-in-constructor-thread-safe was used `final` `HashMap`, and all values were put in constructor. I wonder what will be the difference if there was no `final` modifier. As for me, I see no difference bcs anyway thread won't return object until constructor finishes his work, he didn't pass out `this` out of constructor. Or bcs it is not synchronized `Map` thread can cache values which he put into `Map` and other threads won't see fully populated map but only part of it? – zexed640 Nov 14 '21 at 12:57
  • `final fields also allow programmers to implement thread-safe immutable objects without synchronization` why do I even have to use synchronization inside of constructor if only single thread can execute code in it? – zexed640 Nov 14 '21 at 13:00
  • Does it mean that if I will used `final` on unsynchronized collection, values put in during construction won't be cached inside of thread but will be commited in main memory despite there is no synchronization and collection isn't thread-safe, and if there will be no `final` modifier there is possibility that values put in will be visible only to thread which executed code in constructor? – zexed640 Nov 14 '21 at 13:04
  • 1
    1) Yes. If you satisfy **all** of the prerequisites. 2) Yes. – Stephen C Nov 14 '21 at 13:12
  • I modifed code a bit `new _Thread(new Dummy()).start()`, but didn't achieved expected result. Every time it prints that list has all 10_000 elements. Does it happens because `start()` methods triggers `happens-before` and all values from constructor in main thread will be visible in `_Thread` or it is just hard to reproduce? – zexed640 Nov 14 '21 at 13:19
  • If you are trying to demonstrate that the code is not thread-safe, be aware that the `println` *could* be altering the way that things are working. Also, be aware that thread non-safe behavior may actually occur on particular platforms under particular circumstances that are hard to reproduce. They may depend on hardware behavior, numbers of cores, specific code sequences generated by specific JIT compilers, etc. (The fact that you are not able to show thread non-safety does NOT mean that code is thread-safe. You have to carefully analyze the code against what the JLS says.) – Stephen C Nov 14 '21 at 13:30