[concurrency-interest] synchronized constructors

Roland Kuhn rk at rkuhn.info
Sun Dec 18 18:15:53 EST 2011


Thank you, David, that clarifies a lot, including which references NOT to read ;-) Back to the drawing board, then. 


Regards,

Roland Kuhn
Typesafe — Enterprise-Grade Scala from the Experts
twitter: @rolandkuhn

On 18 dec 2011, at 23:29, "David Holmes" <davidcholmes at aapt.net.au> wrote:

> Roland,
> 
> "roach motel" are informal semantics - they roughly describe the effects of
> the rules but they do not define actual rules. Your reference back to the
> JVMS is to JVMS 2nd edition, Chapter 8 and that is obsolete - it dates back
> to Java 1.2 if I recall correctly and describes the old Java Memory Model.
> The new JMM came out for Java 5 and updates JLS 3rd Edition Chapter 17 and
> replaces JVMS Chapter 8 (which was just a duplicate of the JLS chapter).
> 
> In the "new" JMM you have to establish that a happens-before ordering exists
> between two actions - eg for a read to see a specific value that was
> written, you must establish that the write happens-before the read. This is
> not a simple temporal relationship (that the write happened to occur prior
> to the read as seen by an external observer) but a very formal one: if there
> is no happens-before edge between a write and a read then the read need not
> see the value that was written (even if it physically occurred afterwards).
> 
> "roach motel" basically describes the effects of the happens-before rules as
> they apply to actions on volatiles and action on monitors. If actions can
> move in, it is because there is no happens-before ordering to prevent it;
> while actions can not move out because there is a happens-before ordering
> that prevents it.
> 
> So in your code you can not use the empty synchronized block to enforce an
> ordering constraint across threads because no other thread ever acquires the
> same monitor and so there is no happens-before relationship anywhere.
> 
> In practice you will get whatever synchronization artefacts the VM inserts,
> but this is not something you should be relying on. Lock-elision, as already
> discussed, must preserve happens-before orderings, but here there are none,
> so lock-elision can completely remove all traces of the synchronized block.
> 
> Hope that clarifies things.
> 
> David
> -----
> 
>> -----Original Message-----
>> From: Roland Kuhn [mailto:rk at rkuhn.info]
>> Sent: Monday, 19 December 2011 1:10 AM
>> To: dholmes at ieee.org
>> Cc: concurrency-interest at cs.oswego.edu
>> Subject: Re: [concurrency-interest] synchronized constructors
>> 
>> 
>> Yes, inspired by the JSR-133 cookbook I was alluding to roach
>> motel semantics: the synchronized is just there to ensure that
>> the volatile write has happened before the possibly non-volatile
>> write of the newly constructed object’s reference to a possibly
>> shared location is actually performed. This would mean that
>> nobody can obtain a reference before initialization is done. And
>> then I would like to arrive at the conclusion that the volatile
>> read of “x” from a different thread must synchronize-with the
>> initial write.
>> 
>> Does this work?
>> 
>> Roland
>> 
>> On Dec 18, 2011, at 00:40 , David Holmes wrote:
>> 
>>> Roland,
>>> 
>>> An empty synchronized block in a constructor, when there are no other
>>> synchronized regions of code, gains you nothing under the JMM
>> as there are
>>> no happens-before edges established. Are you
>>> relying on the implementation of synchronized to issue specific memory
>>> barriers here?
>>> 
>>> David
>>> 
>>>> -----Original Message-----
>>>> From: Roland Kuhn [mailto:rk at rkuhn.info]
>>>> Sent: Sunday, 18 December 2011 1:46 AM
>>>> To: dholmes at ieee.org
>>>> Cc: Zhong Yu; concurrency-interest at cs.oswego.edu
>>>> Subject: Re: [concurrency-interest] synchronized constructors
>>>> 
>>>> 
>>>> 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
>>>> 
>>>> 
>>> 
>> 
>> --
>> I'm a physicist: I have a basic working knowledge of the universe
>> and everything it contains!
>>    - Sheldon Cooper (The Big Bang Theory)
>> 
>> 
> 



More information about the Concurrency-interest mailing list