2

Could you explain how the value of f.y could be seen 0 instead of 4? Would that be because other thread writes updates the value to 0 from 4? This example is taken from jls https://docs.oracle.com/javase/specs/jls/se8/html/jls-17.html#jls-17.5

 class FinalFieldExample { 
        final int x;
        int y; 
        static FinalFieldExample f;

        public FinalFieldExample() {
            x = 3; 
            y = 4; 
        } 

        static void writer() {
            f = new FinalFieldExample();
        } 

        static void reader() {
            if (f != null) {
                int i = f.x;  // guaranteed to see 3  
                int j = f.y;  // could see 0
            } 
        } 
    }
Pushparaj
  • 1,039
  • 1
  • 6
  • 26
  • 2
    "guaranteed to see 3" this is not true too, java does not even consider most of fields really "final", as can be changed via reflections or field can be read before initialising it in constructor. – GotoFinal Jul 16 '18 at 15:27
  • Also you didn't describe what you are doing here, when that `reader` method will be executed? – GotoFinal Jul 16 '18 at 15:32
  • 1
    @GotoFinal Your assertions about "not really final" are... just not correct. – chrylis -cautiouslyoptimistic- Jul 16 '18 at 15:36
  • 1
    @chrylis I think the main point here is that despite `x` being `final`, it can be seen with two different values from another thread: with `0` before `x = 3;` was executed, and with `3` afterwards. – Jaroslaw Pawlak Jul 16 '18 at 15:54
  • @JaroslawPawlak nope, this is the case for `y`, but we are not talking about reflection, it's just about safe publication and final fields. – grape_mao Jul 16 '18 at 15:58
  • @chrylis explain then? final field should never change value in runtime - in java it does, as it is just normal field that just needs to be initialised in constructor, but you can use that object before constructor finish and then other objects can see that value change. Also you can just change field using reflections - and java does not track this, so it also is unable to do bigger optimalizations to final fields. There was even once proposal to change this: http://mail.openjdk.java.net/pipermail/hotspot-compiler-dev/2015-June/018342.html – GotoFinal Jul 16 '18 at 16:08
  • @GotoFinal To be clear, though, the "guaranteed to see 3" comment is from the JLS. https://docs.oracle.com/javase/specs/jls/se10/html/jls-17.html#d5e35661 – Radiodef Jul 16 '18 at 16:11
  • @Radiodef ah, now it makes more sense about real issue. So this can be 0 from other thread due to CPU cache etc, but for final fields it is guaranteed to not be an issue. – GotoFinal Jul 16 '18 at 16:13
  • @JaroslawPawlak No, you are *guaranteed* not to see `x == 0` anywhere. – chrylis -cautiouslyoptimistic- Jul 16 '18 at 16:38
  • 1
    @chrylis anywhere? https://ideone.com/ieKmMz – GotoFinal Jul 16 '18 at 16:59
  • This is already answered here : https://stackoverflow.com/questions/14653739/about-reference-to-object-before-objects-constructor-is-finished – Neerav Vadodaria Jul 17 '18 at 07:58

2 Answers2

4

Assuming that we have two threads started, like this:

new Thread(FinalFieldExample::writer).start(); // Thread #1
new Thread(FinalFieldExample::reader).start(); // Thread #2

We might observe our program's actual order of operations to be the following:

  1. Thread #1 writes x = 3.
  2. Thread #1 writes f = ....
  3. Thread #2 reads f and finds that it is not null.
  4. Thread #2 reads f.x and sees 3.
  5. Thread #2 reads f.y and sees 0, because y does not appear to be written yet.
  6. Thread #1 writes y = 4.

In other words, Threads #1 and #2 are able to have their operations interleave in a way such that Thread #2 reads f.y before Thread #1 writes it.

Note also that the write to the static field f was allowed to be reordered so that it appears to happen before the write to f.y. This is just another consequence of the absence of any kind of synchronization. If we declared f as also volatile, this reordering would be prevented.


There's some talk in the comments about writing to final fields with reflection, which is true. This is discussed in §17.5.3:

In some cases, such as deserialization, the system will need to change the final fields of an object after construction. final fields can be changed via reflection and other implementation-dependent means.

It's therefore possible in the general case for Thread #2 to see any value when it reads f.x.

There's also a more conventional way to see the default value of a final field, by simply leaking this before the assignment:

class Example {
    final int x;

    Example() {
        leak(this);
        x = 5;
    }

    static void leak(Example e) { System.out.println(e.x); }

    public static void main(String[] args) { new Example(); }
}

I think that if FinalFieldExample's constructor was like this:

static FinalFieldExample f;

public FinalFieldExample() {
    f = this;
    x = 3; 
    y = 4; 
} 

Thread #2 would be able to read f.x as 0 as well.

This is from §17.5:

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 more technical sections of specification for final contain wording like that as well.

Radiodef
  • 37,180
  • 14
  • 90
  • 125
  • "Thread #2 reads f.y and sees 0, because y is not yet written.": not because `y` is not yet written, but because `y` is not safely published. The initialization should already be completed when thread 2 reads a new reference of `f`. However whether it reads the up-to-date value of `y` is not guaranteed. – grape_mao Jul 16 '18 at 17:04
  • Reading the default value of a field can also happen without leaking the `this` reference, as long as it is not safely published (which means using volatile, final or in synchronized block etc.) – grape_mao Jul 16 '18 at 17:06
  • *"not because y is not yet written, but because y is not safely published"* The JLS is written in terms of ordering, i.e. in absence of synchronization we may observe the read to happen before the write. – Radiodef Jul 16 '18 at 17:25
  • Well, it seems like I was wrong about the order of reference assignment and the constructor. Thread 2 can see a new reference before the constructor finishes (I'm still not sure though...). What I was trying to explain is even `y` has been set to 4 by thread 1, thread 2 is not guaranteed to see it because of local cache. – grape_mao Jul 17 '18 at 08:33
1

Could you explain how the value of f.y could be seen 0 instead of 4?

In Java, one of the important optimizations performed by the compiler/JVM is the reordering of instructions. As long as it doesn't violate the language specifications, the compiler is free to reorder all instructions for efficiency reasons. During object construction, it is possible for an object to be instantiated, the constructor to finish, and its reference published before all of the fields in the object have been properly initialized.

However, Java language says that if a field is marked as final then it must be properly initialized by the time the constructor finishes. To quote from the section of the Java language specs you reference. Emphasis is mine.

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.

So by the time the FinalFieldExample is constructed and assigned to f, the x field must be properly initialized to 3 however the y field may or may not have been properly initialized. So if thread1 makes the call to writer() and then thread2 makes the call to reader() and sees f as not null, y could be 0 (not yet initialized) or 4 (initialized).

Gray
  • 115,027
  • 24
  • 293
  • 354