6

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

UPDATE

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.

Cœur
  • 37,241
  • 25
  • 195
  • 267
CaptainHastings
  • 1,557
  • 1
  • 15
  • 32
  • Since the construction happened before being passed (how it could be passed otherwise in the first place?), my guess would be it's not possible. – MirMasej Sep 08 '15 at 18:10
  • Can you please clarify if there are getters or not? I think you mean there _are_ getters but _no_ setters, while there's an answer that assumes there are neither. – hiergiltdiestfu Sep 08 '15 at 18:40
  • @hiergiltdiestfu Yes, there are getters but no setters. – CaptainHastings Sep 08 '15 at 20:08
  • 4
    the quote doesn't apply here because there are no final fields. final on the class doesn't affect the individual fields. – Nathan Hughes Sep 08 '15 at 20:09
  • @NathanHughes My thoughts precisely. Since none of the fields are final, there is no visibility guarantee that another thread will see the updated values. I just wish section 16.3 in JCIP was worded a bit clearly. – – CaptainHastings Sep 09 '15 at 16:31
  • In order to avoid hypothetical discussion, I recommend you showing us also the code where `Consumer` and `Producer` exchange data. – V G Sep 14 '15 at 14:00
  • The `final Container c` -- where is that happening? Is it in a method body, or is it declaring and instantiating an instance field in a class? – yshavit Sep 14 '15 at 15:50
  • The updated code just throws NullPointerException for me. Beacuse `client.consume()` in `Thread2` is called before `producer.produce()` in `Thread1` performs `client.update(c)`. Even if `Thread1` is started before `Thread2`. So that is definitely not a way to publish an object safely. – Denis Iskhakov Sep 14 '15 at 22:02

3 Answers3

6
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.

Community
  • 1
  • 1
Denis Iskhakov
  • 344
  • 1
  • 6
  • 1
    But fields are not final! – fps Sep 08 '15 at 20:16
  • Final field in this case is the `final Container c = new Container("Ted Dibiasi", 10, 1000000);` – Denis Iskhakov Sep 08 '15 at 22:32
  • So the container instance is the object referenced by final field of `Thread1`. And memory model guarantees that it is at least up-to-date as the final field `c` if `Thread1` constructor is finished. – Denis Iskhakov Sep 08 '15 at 22:40
  • I just noticed that `final Container c = new Container("Ted Dibiasi", 10, 1000000);` may also be a local variable. In that case the quote is not applicable. But from the question context it seems like a field in `Thread1`. – Denis Iskhakov Sep 08 '15 at 22:55
0

Programmers usually do not need to worry about this issue. It is only a problem if an object is "published unsafely", for example, the object is assigned to a non-volatile static field by Thread-1, and Thread-2 retrieves the object from reading the non-volatile filed. However, this is rarely the case; objects are passed between threads almost always with some memory barrier. For example, you don't need to worry about visibility when you pass the object to a ThreadPoolExecutor.

Unsafe publication should be avoided at all cost, unless you really really need it and you know exactly what you are doing.

A class usually does not need to be designed to withstand unsafe publication, unless there's a good reason to. For example, String is designed this way, because it is used extensively in core security/access control code, and the content of a string must appear to be constant, even if some hostile programs try to sabotage it through unsafe publication.

Most classes do not need to use final field for the sake of withstanding unsafe publication.

ZhongYu
  • 19,446
  • 5
  • 33
  • 61
0

To answer your question, no, Thread2 will never see the fields of Container in an uninitialized state. The reason is that the constructor of Container runs fully before the reference Container c becomes accessible. There might be a race condition between the calls Client.update and Client.consume. Depending on the result of this race, the field c is either null, or a fully initialized Container object at the time of the call c.getName() in Client.consume. In the first case, you get a NullPointerException, in the second case, the correctly initialized value. I don't think it has anything to do with the quoted sentence from JCIP.

sandris
  • 1,363
  • 13
  • 27