11

DZone refcard titled "Core Java Concurrency" states:

Once set, final field values cannot be changed. Marking an object reference field as final does not prevent objects referenced from that field from changing later. For example, a final ArrayList field cannot be changed to a different ArrayList, but objects may be added or removed on the list instance.

and

Final field freeze includes not just the final fields in the object but also all objects reachable from those final fields.

I am not entirely clear about the second statement. Does this mean that if I have a final field in class A of type Class B, which in turn have a final field of type Integer, then final field freeze for an instance of class A completes only after the final field freeze for b.c have already happened?

public class A{

  public final B b = new B();

}

public class B{ 

  public final Integer c = 10;

}
Alex Miller
  • 69,183
  • 25
  • 122
  • 167
Tahir Akhtar
  • 11,385
  • 7
  • 42
  • 69

5 Answers5

11

Does this mean that if I have a final field in class A of type Class B, which in turn have a final field of type Integer, then final field freeze for an instance of class A completes only after the final field freeze for b.c have already happened?

I think I would carefully say that final field freeze in this case means that when you create an instance of A and safely publish it, other objects will never see an uninitialized value for b or c.

I would also say that when you are creating the instance of B inside A, other initialization code inside A will never see an uninitialized value for c.

One case where I have encountered real questions around final field freeze is for example a class that contains a (mutable) HashMap, intended only for read, initialized during construction:

public class DaysOfWeek {
    private final Map daysOfWeek = new HashMap();
    public DaysOfWeek() { 
      // prepopulate my map
      daysOfWeek.put(0, "Sunday");
      daysOfWeek.put(1, "Monday");
      // etc
    }

    public String getDayName(int dayOfWeek) {
      return daysOfWeek(dayOfWeek);
    }
}

The question arises here: assuming this object is safely published, and given that there is no synchronization here, is it safe for other threads to call getDayName()? The answer is yes, because final field freeze guarantees that the HashMap and everything reachable from it (here it's just strings, but could be arbitrarily complex objects) is frozen at the end of construction. [If you want to actually modify this map after construction, then you'll need explicit synchronization around reads and writes.] Here's a lengthier blog exploring the topic and check the comments for some interesting responses by people like Brian Goetz.

btw I'm the author of the refcard

Alex Miller
  • 69,183
  • 25
  • 122
  • 167
  • 1
    Thanks Alex for the detailed answer. Really appreciated. BTW I really liked the Refcard as well. – Tahir Akhtar Dec 15 '10 at 19:04
  • 2
    >>...final field freeze in this case means that when you create an instance of A and safely publish it, other objects will never see an uninitialized value for b or c<< - no, actually "safely publishing" is not requaired here. The only requirement: 'this' must not escape from the constructor – Valentin Kovalenko Apr 25 '13 at 18:14
  • @Alex Miller "Final field freeze" is an official term ? – nish1013 Oct 09 '13 at 14:11
  • @nish1013 yes - http://docs.oracle.com/javase/specs/jls/se7/html/jls-17.html#jls-17.5.1 – Alex Miller Oct 11 '13 at 20:25
  • @AlexMiller if only that specification would be human readable... I've tried reading it a couple of times, still no luck. I *think* this freeze action is here : *Given a write w, a freeze f, an action a (that is not a read of a final field), a read r1 of the final field frozen by f, and a read r2 such that hb(w, f), hb(f, a), mc(a, r1), and dereferences(r1, r2), then when determining which values can be seen by r2, we consider hb(w, r2)* – Eugene May 24 '18 at 15:06
7

Java Concurrency in Practice mentions this in section 16.3:

Initialization safety guarantees that for properly constructed objects, all threads will see the correct values of final fields that were set by the constructor, regardless of how the object is published. Further, any variables that can be reached through a final field of a properly constructed object (such as the elements of a final array or the contents of a HashMap referenced by a final field) are also guaranteed to be visible to other threads. For objects with final fields, initialization safety prohibits reordering any part of construction with the initial load of a reference to that object. All writes to final fields made by the constructor, as well as to any variables reachable through those fields, become “frozen” when the constructor completes, and any thread that obtains a reference to that object is guaranteed to see a value that is at least as up to date as the frozen value. Writes that initialize variables reachable through final fields are not reordered with operations following the post-construction freeze.

Vladimir Ivanov
  • 42,730
  • 18
  • 77
  • 103
  • What exactly author meant by "regardless of how the object is published"? I believe properly constructed itself means whose reference do not escape during construction. – saharsh-jain Oct 24 '17 at 15:44
  • According to the book the "properly constructed object" means that "[the] this reference does not escape during construction" whereas the publishing means "making [an object] available to code outside of its current scope". – Piotr Michalczyk May 19 '23 at 12:24
2

Right. That follows from JMM

Look for paragraph:

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.

Since constructor will not be finished until class B initializes that guarantee freeze of B.c

Petro Semeniuk
  • 6,970
  • 10
  • 42
  • 65
2

The guarantee is stronger than you appear to think. The final field semantics apply even to mutable objects that are assigned to final fields (with the usual restrictions). SO extending your example to make A.b private and B mutable (but not externally mutable).

public class A {
    private final B b = new B();
    public Integer get() { return b.c; }
}

public class B {
    public Integer c = 10;
}

In this case, A.get will never return null even under unsafe publication. Of course this example is completely abstract and therefore meaningless. Typically it is important for arrays (for instance in String) and collections.

Tom Hawtin - tackline
  • 145,806
  • 30
  • 211
  • 305
  • if you put your final init at the end of constructor and have a bad code that leaks this from constructor, then you can get null – bartosz.r Mar 30 '11 at 14:10
1

It does not really make sense to talk about what becomes final before what else. To your program, once your object is created (actually from the moment the field is assigned once) the reference can not change anymore. Since the B instance is created before the A instance, you could say c becomes final before b, but it does not really matter.

Where the order is important is when you have multiple final fields in a single class. If you want to use the value of one final field in the assignment of another, you should only access fields that already have been initialized.

To be honest, that 'final field freeze' sentence does not make much sense to me.

jackrabbit
  • 5,525
  • 1
  • 27
  • 38