Visibility Guarantee

I read a few explanations of section 16.3, "Initializing Security" from JCIP, and is still unclear. The section indicates that

"In addition, any variables that can be reached through the final field of a properly constructed object (for example, the elements of the final array or the HashMap contents referenced by the final field) are visible to other threads are also guaranteed."

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 the following and passes c to Thread2 .

 final Container c = new Container("Ted Dibiasi", 10, 1000000); 

Thread2 (not at the same time, say, after 1 ms), reads the 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? 

Greetings

UPDATE

I noticed that there are getters in the class. I am updating the source code, hope it will be clear. Thanks to everyone for watching.

 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:

a) When Thread2 calls consume (), can you call cupsWon, netWorth ever equal to null, 0 or 0.0? I thought it was CAN , because the fields in the Container class are not final, there is no guarantee of visibility.

b) However, then I read section 16.3 and a bit about “ variables that can be achieved through the final field of a correctly constructed object ”, does this mean that, since the Container c instance is declared final, we DO have a guarantee of visibility in consumption ()?

final Container c = new container ("Ted Dibiasi", 10, 1,000,000);

c) Declaring a reference to Container in the Client class as a mutable solution to the problem of visibility of fields related to the standard.

+6
source share
3 answers
 final Container c = new Container("Ted Dibiasi", 10, 1000000); 

If c here is the final field in Thread1 , and not a local variable, then the citation from the Java Language Specification is applied for this final field c .

An object is considered fully initialized when its constructor finishes. A stream that can only see a reference to an object after this object has been fully initialized is guaranteed to see correctly initialized values ​​for the final fields of this object .

The model for using finite fields is simple: set the final fields for an object in this object constructor; and do not write a link to an object that is being built in a place where another thread can see it until the constructor of the object completes. If this is done, then when the object is seen by another thread, this thread will always see a correctly constructed version of the final fields of this object. He will also see versions of any object or array referenced by those trailing fields that are at least relevant as last fields.

Although the wording here is vague, I think that “correctly initialized value” and “last as final field” means that if you pass c to Thread2 outside the constructor of Thread1 , Thread2 will always see a fully constructed instance of Container with its fields initialized.

+6
source

Programmers usually do not need to worry about this problem. This is a problem only if the object is "published unsafe", for example, the object is bound to the non-volatile static field Thread-1, and Thread-2 extracts the object from reading non-volatile data. However, this is rarely the case; objects are transferred between threads almost always with some memory barrier. For example, you do not need to worry about visibility when you pass a ThreadPoolExecutor object.

Unsafe publishing should be avoided at all costs unless you really need it and you know exactly what you are doing.

Typically, a class should not be designed to withstand unsafe publishing unless there is a good reason. For example, String designed this way because it is widely used in the main security / access control code, and the contents of the string should be constant, even if some hostile programs try to sabotage it through unsafe publishing.

Most classes do not need to use the final field to counter unsafe publishing.

0
source

To answer your question, no, Thread2 will never see the Container fields in an uninitialized state. The reason is that the Container constructor runs all the way up , the Container c reference becomes available. Between calls to Client.update and Client.consume may be a race condition. Depending on the outcome of this race, the c field is either null or a fully initialized Container object during a call to c.getName() in Client.consume . In the first case, you get a NullPointerException, in the second case, a correctly initialized value. I do not think this has anything to do with the quoted suggestion from JCIP.

0
source

All Articles