I have read a few explanations of section 16.3 "Initialization Safety" from JCIP and am still not clear. The section states that
"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."
So if I had the following mutable object:
public final class Container{
private String name;
private int cupsWon;
private double netWorth;
public Container( String name, int cupsWon, double netWorth ){
this.name = name;
this.cupsWon = cupsWon;
this.netWorth = netWorth;
}
//NO Setters
//Getters
}
Then, Thread 1 creates it as following and passes c to Thread2.
final Container c = new Container("Ted Dibiasi", 10, 1000000);
Thread2 (not concurrently, lets say after 1 ms), reads values of c, is it possible that Thread2 will ever see
c.name=null or
c.cupswon=0 or worst of all,
c.netWorth=0.0?
Cheers
I noticed there was some confusion about the class having getters. I am updating the source code, hopefully this will be clear. Thanks all for taking a look.
public final class Container{
private String name;
private int cupsWon;
private double netWorth;
public Container( String name, int cupsWon, double netWorth ){
this.name = name;
this.cupsWon = cupsWon;
this.netWorth = netWorth;
}
public final String getName(){
return name;
}
public final int getCupsWon(){
return cupsWon;
}
public final double getNetWorth(){
return netWorth;
}
}
//----------
public final class Producer{
private final Client client;
public Producer( Client client ){
this.client = client;
}
//Thread1 call produce()
public final void produce( ){
final Container c = new Container("Ted Dibiasi", 10, 1000000);
client.update( c );
}
}
//----
public final class Client{
private Container c;
//private volatile Container c;
public final void update( Container c ){
this.c = c;
}
//Thread2 calls consume().
public final void consume( ){
String name = c.getName();
int cupsWon = c.getCupsWon();
double netWorth = c.getNetWorth();
}
}
My questions are:
a) When Thread2 calls consume(), can name, cupsWon, netWorth ever be null, 0, or 0.0? My thinking was that it CAN because since the fields in Container class are not final, there is no visibility guarantee.
b) However, then I read section 16.3 and the bit about "variables that can be reached through a final field of a properly constructed object", does this mean that because the instance of Container c is declared final, we DO have visibility guarantee in consume()?
final Container c = new Container("Ted Dibiasi", 10, 1000000);
c) Declaring reference to Container in Client class as volatile wont solve the visibility issue of the fields as it pertains to the reference.
In other words, the read of the volatile variable is guaranteed to happen before the two subsequent reads of the non-volatile variables. The last two instructions could be freely reordered among themselves, without violating the happens-before guarantee of the volatile read in the first instruction.
Therefore, the volatile keyword does not provide thread safety when non-atomic operations or composite operations are performed on shared variables. Operations like increment and decrement are composite operations.
Volatile keyword is used to modify the value of a variable by different threads. It is also used to make classes thread safe. It means that multiple threads can use a method and instance of the classes at the same time without any problem.
final Container c = new Container("Ted Dibiasi", 10, 1000000);
If c
here is the final field in Thread1
and not a local variable then a quote from Java Language Specification applies to this final field c
:
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 usage model for final fields is a simple one: Set the final fields for an object in that object's constructor; and do not write a reference to the object being constructed in a place where another thread can see it before the object's constructor is finished. If this is followed, then when the object is seen by another thread, that thread will always see the correctly constructed version of that object's final fields. It will also see versions of any object or array referenced by those final fields that are at least as up-to-date as the final fields are.
Though the wording is vague here, I think that "correctly initialized value" and "up-to-date as the final field" means that if you pass the c
to the Thread2
outside the Thread1
constructor, the Thread2
will always see a fully constructed Container
instance with its fields initialized.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With