[concurrency-interest] synchronized constructors

Roland Kuhn rk at rkuhn.info
Sat Dec 17 10:46:26 EST 2011


Thank you, David and Hans, for clarifying these semantics. The OP’s question has been answered, and I think I can also derive an answer for my question. I’ll try to wrap it up below, please correct or confirm:

class IAmSafe {
  private volatile int x;
  IAmSafe() {
    x = 5;
    synchronized(this) {}
  }
  public int getX() {
    return x;
  }
}

Assuming that the java.lang.Object constructor is well-behaved, it should be impossible under any circumstances (barring reflective modification) that a call to getX() returns anything but 5, right?

If this is true, then the cheapest solution to my multi-stage construction problem—where I completely control the program flow up to the point where I consider the object initialization finished—on x86 processors looks like this:

class ComplexDeps {
  final int someValue;
  ComplexDeps(int someValue) {
    this.someValue = someValue;
  }
  // stage 2
  private volatile OtherThing thing;
  public void init(OtherThing thing) {
    this.thing = thing;
    synchronized {}
  }
  // other stuff which reads someValue & “thing”, never writes to “thing” and runs only after init() in program order
}

IIUC, there will be a (half-way) expensive fence-like instruction emitted at the end of init(), but apart from that all read accesses after construction should be cheap. Any nobody will ever see “thing” uninitialized.

Regards,

Roland

On Dec 17, 2011, at 09:57 , David Holmes wrote:

> Correction ...
> I wrote:
>> Zhong Yu writes:
>>> Suppose constructor java.util.Vector() is synchronized
>>> 
>>>    static Vector v;
>>> 
>>>    // thread 1
>>>    v = new Vector();
>>> 
>>>    // thread 2
>>>    Vector r = v;
>>>    if(r!=null)
>>>      r.add(x);
>>> 
>>> The last line can throw exception, because thread 2 can observe the
>>> blank state of the object
>> 
>> No it can not.
>> Completion of the constructor happens-before the reference is published.
> 
> Delete this sentence. It's wrong but not relevant to the argument.
> 
> David
> -----
> 
> 
>> The acquiring of the monitor in add() can only happen when the
>> monitor is released by the constructor. The release of the
>> monitor in thread
>> 1 happens-before the acquire of the monitor by thread 2. All the
>> constructor
>> actions happen-before the release of the monitor.
>> 
>> The publishing of the Vector can not be moved prior to the acquisition of
>> the monitor in the constructor ("roach motel semantics").
>> 
>> David
>> -----
>> 
>> 
>>> , yet Vector.add() presumes a> post-construction state.
>> 
>>> Zhong Yu
>>> 
>>> On Fri, Dec 16, 2011 at 7:52 PM, David Holmes
>>> <davidcholmes at aapt.net.au> wrote:
>>>> Zhong Yu writes:
>>>>> If the reference is unsafely published, another thread can get the
>>>>> reference early; it then calls an instance method which may
>> obtain the
>>>>> lock before the creation thread can obtain the lock for the
>>>>> constructor. Therefore the other thread can observe the blank state.
>>>>> As Ruslan corrected me, no partial state can be observed though.
>>>> 
>>>> The only way this can happen, if you synchronize the whole
>>> constructor body,
>>>> is if a super class constructor does the unsafe publishing. But
>>> in that case
>>>> there is no such thing as safe-publishing because the object
>> can escape
>>>> before the subclass constructor does any initialization.
>>>> 
>>>> To summarize a long and garbled thread. If the constructor body is
>>>> synchronized, there is no accessible state and all methods are
>>> synchronized,
>>>> then no external user of the class can publish a reference in a
>>> way that is
>>>> unsafe. If the constructor does the publishing within the synchronized
>>>> block, it is still safe. Only if the superclass does it can it
>>> possibly be
>>>> unsafe.
>>>> 
>>>> Also to address an other point: lock elision is allowed (eg
>> using escape
>>>> analysis) but the memory synchronization effects must remain
>>> (you can lose
>>>> enforced mutual exclusion [as you don't need it], but not
>> happens-before
>>>> edges).
>>>> 
>>>> Constructors can't be synchronized simply because back in 1995 no one
>>>> realized there could be a need for it. It can be mostly
>> worked around by
>>>> using a synchronized block. But you can't synchronize the
>>> invocation of the
>>>> super constructor.
>>>> 
>>>> End of story :)
>>>> 
>>>> Cheers,
>>>> David
>>>> 
>>> 
>> 
>> _______________________________________________
>> Concurrency-interest mailing list
>> Concurrency-interest at cs.oswego.edu
>> http://cs.oswego.edu/mailman/listinfo/concurrency-interest
>> 
> 
> _______________________________________________
> Concurrency-interest mailing list
> Concurrency-interest at cs.oswego.edu
> http://cs.oswego.edu/mailman/listinfo/concurrency-interest

--
Simplicity and elegance are unpopular because they require hard work and discipline to achieve and education to be appreciated.
  -- Dijkstra




More information about the Concurrency-interest mailing list