[concurrency-interest] synchronized constructors

Kearney, Joe Joe.Kearney at gsacapital.com
Fri Dec 16 04:59:11 EST 2011

How much of that locking is allowed to be elided away?

If you call synchronized(this) {} in a constructor, I would argue that provably no other thread can acquire the lock yet. So can the locking be compiled away, even though the induced fence would need to stay?

-----Original Message-----
From: concurrency-interest-bounces at cs.oswego.edu [mailto:concurrency-interest-bounces at cs.oswego.edu] On Behalf Of Roland Kuhn
Sent: 16 December 2011 09:49
To: Zhong Yu
Cc: concurrency-interest
Subject: Re: [concurrency-interest] synchronized constructors

As I was recently made aware: the rules for write reordering are not as weak as you think.


Section 8.8 of the JVM spec requires that a write cannot be moved earlier across a "lock" operation. This means that a synchronized {} in the constructor solves the issue.



On Dec 16, 2011, at 08:58 , Zhong Yu wrote:

> I'm totally sympathetic to this design goal - if an object claims to 
> be thread safe, it should survive unsafe publication without problems.
> I believe all java.util.concurrent classes have this property.
> However, `synchronized` constructor cannot help to reach that goal.
>    foo = new Foo();
> is roughly equivalent to
>    [A]    tmp = allocate memory space for sizeof(Foo)
>    [B]    zero the space. (this may occur before [A])
>    [1]    tmp.Foo(); // as if the constructor is an instance method
>    [2]    foo = tmp;
> [2] can be reordered before [1], so other threads may observe 
> blank/partial state through `foo` reference. This reordering is 
> allowed even if `Foo()` is synchronized.
> This problem affects some "thread safe" classes like java.util.Vector.
> If a Vector object is unsafely published, program can crash 
> unexpectedly. I bet that's a huge surprise to most Java programmers.
> But I don't think the "unenlightened mass" are to blame. We have this 
> strong intuition that an object comes to existence only after 
> construction. Nobody outside of construction should observe a 
> partially constructed object (unless `this` is leaked during 
> construction).
> If JMM has this simple "whole-birth" guarantee, many man-hours of 
> anguish will be saved. You can innocently do a double checked locking, 
> and nobody is going to yell at your naivety.
> Unfortunately JMM doesn't have this guarantee. Allegedly such 
> guarantee carries a performance penalty. The only work around is 
> through `final` fields. Neither `synchronized` nor `volatile` is 
> useful for this purpose (many people are mistaken on this point; it is 
> due to their whole-birth intuition, subconsciously adding a 
> synchronization order from end of constructor to begin of instance
> methods)
> We are encouraged to use `final` fields whenever we can. One crucial 
> reason is for the memory effect guarantee. However, using `final` 
> fields will incur the same performance penalty of whole-birth 
> guarantee. We have a great contradiction here. Either `final` is not 
> cheap, so we should avoid `final` fields if we can; or `final` is 
> cheap, so whole-birth guarantee can be established cheaply, yielding 
> `final` unnecessary.
> JMM does guarantee that [2] cannot be reordered before [B], otherwise 
> other threads can observe garbage state. Let's call this the "blank"
> guarantee. It's also a mystery to me why whole-birth guarantee can be 
> fundamentally more expensive than blank guarantee.
> Note that not everybody promotes the use of `final` though. We can 
> sense how reserved David Holmes is through his words: " #Sometime# a 
> class needs to add #some# protection against unsafe-publication that 
> might violate #important# semantic guarantees of the object"
> (We had a similar discussion in a September thread titled "Interesting 
> anti-pattern with volatile"; however I cannot find it on the mailing 
> list archive, so no link.)
> Zhong Yu
> P.S. a "fix" to Vector's vulnerability to unsafe publication
> class Vector
>    final Params ctorParams;
>    Vector(params)
>        ctorParams = new Params(params);
>    boolean initDone;
>    void ensureInit()
>        if(!initDone)
>            initialize this with ctorParams ...
>            initDone = true;
>    synchronized
>    public Foo anyOtherMethod(Bar)
>        ensureInit();
>        actual work...
> Of course, this solution sucks. The role of constructor is severely weakened.
> _______________________________________________
> Concurrency-interest mailing list
> Concurrency-interest at cs.oswego.edu
> http://cs.oswego.edu/mailman/listinfo/concurrency-interest

[scala-debate on 2009/10/2]
Viktor Klang: When will the days of numerical overflow be gone?
Ricky Clarkson: One second after 03:14:07 UTC on Tuesday, 19 January 2038

Concurrency-interest mailing list
Concurrency-interest at cs.oswego.edu

More information about the Concurrency-interest mailing list