[concurrency-interest] synchronized constructors

David Holmes davidcholmes at aapt.net.au
Sun Dec 18 17:29:41 EST 2011


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