24

Someone somewhere told me that Java constructors are synchronized so that it can't be accessed concurrently during construction, and I was wondering: if I have a constructor that stores the object in a map, and another thread retrieves it from that map before its construction is finished, will that thread block until the constructor completes?

Let me demonstrate with some code:

public class Test {
    private static final Map<Integer, Test> testsById =
            Collections.synchronizedMap(new HashMap<>());
    private static final AtomicInteger atomicIdGenerator = new AtomicInteger();
    private final int id;

    public Test() {
        this.id = atomicIdGenerator.getAndIncrement();
        testsById.put(this.id, this);
        // Some lengthy operation to fully initialize this object
    }

    public static Test getTestById(int id) {
        return testsById.get(id);
    }
}

Assume that put/get are the only operations on the map, so I won't get CME's via something like iteration, and try to ignore other obvious flaws here.

What I want to know is if another thread (that's not the one constructing the object, obviously) tries to access the object using getTestById and calling something on it, will it block? In other words:

Test test = getTestById(someId);
test.doSomething(); // Does this line block until the constructor is done?

I'm just trying to clarify how far the constructor synchronization goes in Java and if code like this would be problematic. I've seen code like this recently that did this instead of using a static factory method, and I was wondering just how dangerous (or safe) this is in a multi-threaded system.

Brian
  • 17,079
  • 6
  • 43
  • 66
  • Are you thinking of static initialisation? Mutable statics in general and self-registering constructors in particular are almost always a bad idea. – Tom Hawtin - tackline Sep 26 '12 at 22:44
  • @TomHawtin-tackline I know that properly synchronized static factory methods are the preferred way of doing it, I was just wondering if there were any functional issues here with this code or if the code could actually break. – Brian Sep 26 '12 at 22:47
  • I've undeleted my rushed (and previously wrong) answer @Brian FYI. One important point is that when you call `synchronized` on _any_ object (or if you reference a `volatile` field), _all_ memory is synchronized between threads. Not just the object you `synchronized` on. – Gray Sep 27 '12 at 12:36

6 Answers6

25

Someone somewhere told me that Java constructors are synchronized so that it can't be accessed concurrently during construction

This is certainly not the case. There is no implied synchronization with constructors. Not only can multiple constructors happen at the same time but you can get concurrency issues by, for example, forking a thread inside of a constructor with a reference to the this being constructed.

if I have a constructor that stores the object in a map, and another thread retrieves it from that map before its construction is finished, will that thread block until the constructor completes?

No it won't.

The big problem with constructors in threaded applications is that the compiler has the permission, under the Java memory model, to reorder the operations inside of the constructor so they take place after (of all things) the object reference is created and the constructor finishes. final fields will be guaranteed to be fully initialized by the time the constructor finishes but not other "normal" fields.

In your case, since you are putting your Test into the synchronized-map and then continuing to do initialization, as @Tim mentioned, this will allow other threads to get ahold of the object in a possibly semi-initialized state. One solution would be to use a static method to create your object:

private Test() {
    this.id = atomicIdGenerator.getAndIncrement();
    // Some lengthy operation to fully initialize this object
}

public static Test createTest() {
    Test test = new Test();
    // this put to a synchronized map forces a happens-before of Test constructor
    testsById.put(test.id, test);
    return test;
}

My example code works since you are dealing with a synchronized-map, which makes a call to synchronized which ensures that the Test constructor has completed and has been memory synchronized.

The big problems in your example is both the "happens before" guarantee (the constructor may not finish before Test is put into the map) and memory synchronization (the constructing thread and the get-ing thread may see different memory for the Test instance). If you move the put outside of the constructor then both are handled by the synchronized-map. It doesn't matter what object it is synchronized on to guarantee that the constructor has finished before it was put into the map and the memory has been synchronized.

I believe that if you called testsById.put(this.id, this); at the very end of your constructor, you may in practice be okay however this is not good form and at the least would need careful commenting/documentation. This would not solve the problem if the class was subclassed and initialization was done in the subclass after the super(). The static solution I showed is a better pattern.

Gray
  • 115,027
  • 24
  • 293
  • 354
  • I know multiple constructors can be invoked simultaneously, I didn't mean class-level synchronization, but object-level, like `synchronized(this)`. And no, the synchronized map presumably only locks on its methods so that `get` and `put` are atomic and I don't get weird behavior from concurrent gets/puts. – Brian Sep 26 '12 at 22:30
  • The big problem @Brian is both "happens before" guarantees and memory synchronization. Both are handled by the synchronized-map. It doesn't matter what it is locking on to guarantee that the constructor has finished before it was put into the map. – Gray Sep 26 '12 at 22:31
  • 1
    How does the fact that the map is synchronized prevent someone from getting a reference to a partially-initialized class instance? – DaoWen Sep 26 '12 at 22:34
  • Because the `SynchronizedMap` calls `synchronized`. There is a happens before guarantee when you cross that memory barrier. So the compiler is not able to reorder instructions _past_ the `synchronized` block. So the constructor has to finish _before_ the put(). – Gray Sep 26 '12 at 22:37
  • 1
    Exactly what @DaoWen said, the map synchronizes on itself, not my object, and if my object is put in the map _before_ my constructor finishes (take a look, a lengthy op happens _after_ the `put`), a call to `get` can return its reference and it won't block (since synchronization monitors aren't checked on reference passing, only dereferencing/method invocation). But I see the point you're making otherwise. – Brian Sep 26 '12 at 22:38
  • @Gray - Look at his constructor code again. Even without reordering, there's a bunch of stuff that happens after the put. You'd also still have potential problems with subclasses of `Test` calling `super()`. – DaoWen Sep 26 '12 at 22:39
  • It doesn't matter _what_ object you synchronize on @Brian. _Any_ synchronization causes a memory barrier for _all_ objects. – Gray Sep 26 '12 at 22:39
  • Sorry @DaoWen and all. I completely missed the extra initialization after the `put` which was the whole point of the question. Duh. I initially removed my answer but I've undeleted it and tuned it because I think it still has some value for posterity. – Gray Sep 27 '12 at 12:27
  • **final and volatile fields will be guaranteed to be fully initialized by the time the constructor finishes but not other "normal" fields.** it is wrong. In case of race publication you can see object reference without correctly initialized volatile field. only final gives this guarantee – gstackoverflow Mar 13 '17 at 20:28
  • @Gray, please check http://stackoverflow.com/a/42155096/2674303 Alexey some times ago was teammate of Oracle performance team – gstackoverflow Mar 14 '17 at 20:30
  • Thanks much @gstackoverflow, after reading a bunch of stuff I get it and I've edited my answer. The trick is that a _store_ of a `volatile` field _can_ be reordered with a load or store of a normal field. So it is possible for the store of an object's reference can happen and then the `volatile` field write. Interesting. I wonder if you can do something like a `volatile int x; public A() { x = 43; int y = x; }` to force a _load_ of a `volatile` in the constructor to force publication. Thanks much for the correction. – Gray Mar 15 '17 at 13:30
  • @Gray You are welcome. Happy that could to improve knowledge of so experienced person. – gstackoverflow Mar 15 '17 at 13:38
  • Very important thing that 1.if you see that volatile variable was changed it means that all actions before are visible for you and **2.It is possible(!!!) that actions occured after volatile read wiil be visible before volatile write** – gstackoverflow Mar 15 '17 at 16:24
15

Someone somewhere told me that Java constructors are synchronized

'Somebody somewhere' is seriously misinformed. Constructors are not synchronized. Proof:

public class A
{
    public A() throws InterruptedException
    {
        wait();
    }

    public static void main(String[] args) throws Exception
    {
        A a = new A();
    }
}

This code throws java.lang.IllegalMonitorStateException at the wait() call. If there was synchronization in effect, it wouldn't.

It doesn't even make sense. There is no need for them to be synchronized. A constructor can only be invoked after a new(), and by definition each invocation of new() returns a different value. So there is zero possibility of a constructor being invoked by two threads simultaneously with the same value of this. So there is no need for synchronization of constructors.

if I have a constructor that stores the object in a map, and another thread retrieves it from that map before its construction is finished, will that thread block until the constructor completes?

No. Why would it do that? Who's going to block it? Letting 'this' escape from a constructor like that is poor practice: it allows other threads to access an object that is still under construction.

user207421
  • 305,947
  • 44
  • 307
  • 483
  • 1
    To clarify, my question is about constructor synchronization at the object-level (i.e., `synchronized(this)`) not class-level (`synchronized(Test.class)`). I was essentially asking, "If another thread got my `Test` reference while it was still being constructed, could it call methods without being blocked?" Clearly, the answer is yes, other threads wouldn't be blocked, so +1 – Brian Sep 27 '12 at 13:53
  • @Brian I understood your question. As it is impossible for a constructor ever to execute twice with the same value of 'this', either concurrently or even sequentially, the need for synchronisation on 'this' cannot possibly arise. – user207421 Sep 27 '12 at 17:33
  • Sure it can. Picture this: `Test` has an inner member class, `TestThread`, that calls methods on the `Test` that created it. In my `Test` constructor, I create and start a `TestThread` before performing some lengthy operation. Without synchronization on the constructor for `Test`, `TestThread` runs methods on `Test` while the `Test` is still being constructed. If I synchronize the `Test` in its constructor, the thread has to wait until the constructor is finished before it can call its methods. Does that make sense? – Brian Sep 27 '12 at 17:43
  • @Brian Of course it makes sense, but it isn't an example of two threads invoking the same constructor on the same object at the same time. – user207421 Nov 18 '12 at 01:12
  • You're right, it's not. Instead, it's an example of one thread invoking the constructor while another thread accesses it. *That's* what my question is about: are there any synchronization mechanisms to handle improper reference publishing? I know you can't call a constructor on an object twice, that's obvious. Please re-read my question and take a look at the selected answer. – Brian Nov 20 '12 at 16:44
  • @Brian Nevertheless my answer remains correct. Constructors are not synchronized, and any code pattern that relies on them being synchronized for whatever purpose is exercising undefined behaviour. – user207421 Aug 30 '14 at 05:55
13

You've been misinformed. What you describe is actually referred to as improper publication and discussed at length in the Java Concurrency In Practice book.

So yes, it will be possible for another thread to obtain a reference to your object and begin trying to use it before it is finished initializing. But wait, it gets worse consider this answer: https://stackoverflow.com/a/2624784/122207 ... basically there can be a reordering of reference assignment and constructor completion. In the example referenced, one thread can assign h = new Holder(i) and another thread call h.assertSanity() on the new instance with timing just right to get two different values for the n member that is assigned in Holder's constructor.

Community
  • 1
  • 1
Tim Bender
  • 20,112
  • 2
  • 49
  • 58
  • Gray deleted his answer, so you might want to change your reference to it. – DaoWen Sep 26 '12 at 22:53
  • Thanks for your answer, Tim. I almost don't want to accept and ruin your 6666 rep :) Also, thanks for reminding me to look for that book. We had it at my last job and I never got to read it, I had nearly forgotten about it. – Brian Sep 26 '12 at 23:06
  • NP Brian. @DaoWen, that's a shame, I thought his answer was better. – Tim Bender Sep 26 '12 at 23:11
  • 1
    @Brian, it occurs to me that you may have been recalling behavior associated with assignment of static members and execution of static blocks (`static { /*code here*/ }`). Those actions are performed by the `ClassLoader` and are guaranteed to occur only once (per a ClassLoader) and before publication of any instance of that class. A behavior which could be described as "like synchronization". – Tim Bender Sep 26 '12 at 23:20
  • @TimBender You may well be right. If it's the person that I think told me, it would've been well over a year ago, so it may have lost its original meaning by now. – Brian Sep 26 '12 at 23:23
  • @TimBender - I liked Gray's answer too, but then he went and updated it and added some bad info at the end, and ended up deleting the whole post. It would have been nice if he'd just deleted the bad info and left all the good stuff up. – DaoWen Sep 27 '12 at 00:33
  • 1
    I've re-added my corrected answer guys. I was rushing out the door and missed a key part of the post. Sorry for the confusion. – Gray Sep 27 '12 at 13:08
2

constructors are just like other methods, there's no additional synchronization (except for handling final fields).

the code would work if this is published later

public Test() 
{
    // Some lengthy operation to fully initialize this object

    this.id = atomicIdGenerator.getAndIncrement();
    testsById.put(this.id, this);
}
irreputable
  • 44,725
  • 9
  • 65
  • 93
  • You might want to explain that the atomic access acts as a memory barrier, so it's important that it happens just before the `put`. Also, this would still be unsafe if any classes inherit from `Test` and call `super()`. – DaoWen Sep 26 '12 at 22:57
  • @DaoWen I didn't even think of inheritance. I guess the best option then would definitely have to be some sort of static initialization that caches the result after it has certainly been constructed (e.g. directly after the call to `new`), correct? – Brian Sep 26 '12 at 23:01
  • @Brian - I believe you're right. The only truly safe way to publish an instance of a class while constructing is to construct it using a factory method, which will allow you to publish it only after it's actually finished. Also, I think what I said about the `AtomicInteger` access being important was actually wrong since the map he's doing the `put` on is already synchronized. – DaoWen Sep 27 '12 at 14:57
1

Although this question is answered but the code pasted in question doesn't follow safe construction techniques as it allows this reference to escape from constructor , I would like to share a beautiful explanation presented by Brian Goetz in the article: "Java theory and practice: Safe construction techniques" at the IBM developerWorks website.

Francisco Alvarado
  • 2,815
  • 2
  • 26
  • 51
Vipin
  • 4,851
  • 3
  • 35
  • 65
  • 1
    Answers that provide links with no summaries are generally discouraged. From the [help page](http://stackoverflow.com/help/how-to-answer): "Links to external resources are encouraged, but please add context around the link so your fellow users will have some idea what it is and why it’s there. Always quote the most relevant part of an important link, in case the target site is unreachable or goes permanently offline." – Brian Dec 20 '13 at 20:17
-1

It's unsafe. There are no additional synchronization in JVM. You can do something like this:

public class Test {
    private final Object lock = new Object();
    public Test() {
        synchronized (lock) {
            // your improper object reference publication
            // long initialization
        }
    }

    public void doSomething() {
        synchronized (lock) {
            // do something
        }
    }
}
  • 1
    This is wrong. What if another thread gets the lock in `doSomething()` _before_ the constructor gets the lock? The improper reference publication needs to happen under the lock. However, even after fixing it I wouldn't recommend this method. What if you later extend the Test class and there's more initialization that should be happening under the lock? There are just too many things that can go wrong. – DaoWen Sep 26 '12 at 22:36
  • I agree. But in some specific cases (it's private class, for example) such solution can be applied. – Dmitry Komanov Sep 26 '12 at 23:48
  • OK, I've removed my downvote now that you fixed the synchronized part. If you make the assumption that `super()` will never be called then you should probably declare the class as `final` just to make sure. – DaoWen Sep 27 '12 at 00:30