Why can't Java constructors be synchronized?

前端 未结 9 1467
甜味超标
甜味超标 2020-11-29 18:20

According to the Java Language Specification, constructors cannot be marked synchronized because other threads cannot see the object being created until the thread creating

相关标签:
9条回答
  • 2020-11-29 18:41

    Such a synchronization might make sense in some very rare cases, but I guess, it's just not worth it:

    • you can always use a synchronized block instead
    • it'd support coding in a pretty strange way
    • on what should it synchronize? A constructor is a sort-of static method, it works on an object but gets called without it. So synchronizing on the class also makes (some) sense!

    When in doubt, leave it out.

    0 讨论(0)
  • 2020-11-29 18:42

    Constructor Modifiers section in JLS clearly says

    There is no practical need for a constructor to be synchronized, because it would
    lock the object under construction, which is normally not made available to other
    threads until all constructors for the object have completed their work.
    

    So there is no need for constructor to be synchronized.

    Also it is not recommended to give out the objects reference(this) before object is created. One of the possible ambiguous situations would be to give out the objects reference is superclass constructor when subsclass object is being created.

    0 讨论(0)
  • 2020-11-29 18:44

    Because synchronized guarantees that actions on the same objects are not to be performed by multiple threads. And when the constructor is called you still don't have the object. It is logically impossible for two threads to access the constructor of the same object.

    In your example, even if a method is invoked by the new thread, it is no longer about the constructor - it is about the target method being synchronized or not.

    0 讨论(0)
  • 2020-11-29 18:49

    In your example, the constructor is only actually called once from one thread.

    Yes, it is possible to get a reference to an incompletely constructed Object (some discussions around double check locking and why it is broken reveal this problem), however, not by calling the constructor a second time.

    Syncronized on the constructor would prevent two threads from calling the constructor on the same Object simultaneously, and that is not possible, as it is never possible to call the constructor on an object instance twice, period.

    0 讨论(0)
  • 2020-11-29 18:57

    If you really need synchronization of the rest of the constructor versus any threads which anyhow gets a reference to your not-yet-totally-constructed object, you can use a synchronized-block:

    public class Test {
        public Test() {
           final Test me = this;
           synchronized(this) {
              new Thread() {
                 @Override
                 public void run() {
                    // ... Reference 'me,' the object being constructed
                    synchronized(me) {
                       // do something dangerous with 'me'.
                    }
                 }
              }.start();
              // do something dangerous with this
           }
        }
    }
    

    Usually it is considered bad style to "give out" your not-yet-constructed object like this, so a synchronized constructor is not necessary.


    In some corner cases a synchronized constructor would be useful. Here is a more realistic example, from the discussion of Bozho's answer:

    public abstract class SuperClass {
    
       public SuperClass() {
           new Thread("evil") { public void run() {
              doSomethingDangerous();
           }}).start();
           try {
              Thread.sleep(5000);
           }
           catch(InterruptedException ex) { /* ignore */ }
       }
    
       public abstract void doSomethingDangerous();
    
    }
    
    public class SubClass extends SuperClass {
        int number;
        public SubClass () {
            super();
            number = 2;
        }
    
        public synchronized void doSomethingDangerous() {
            if(number == 2) {
                System.out.println("everything OK");
            }
            else {
                System.out.println("we have a problem.");
            }
        }
    
    }
    

    We want that the doSomethingDangerous() method is only called after construction of our SubClass object is complete, e.g. we only want the "everything OK" output. But in this case, when you only can edit your SubClass, you have no chance of achieving this. If the constructor could be synchronized, it would solve the problem.

    So, what we learn about this: never do something like I did here in the superclass constructor, if your class is not final - and don't call any non-final methods of your own class from your constructor.

    0 讨论(0)
  • 2020-11-29 19:01

    I see little reason to forbid constructors to be synchronized. It would be useful in many scenarios in multi-threaded applications. If I understand the Java Memory Model correctly (I read http://jeremymanson.blogspot.se/2008/11/what-volatile-means-in-java.html) the following simple class could have benefited from a synchronized constructor.

    public class A {
        private int myInt;
    
        public /*synchronized*/ A() {
            myInt = 3;
        }
    
        public synchronized void print() {
            System.out.println(myInt);
        }
    }
    

    In theory, I believe a call to print() could print "0". This could happen if an instance of A is created by Thread 1, the reference to the instance is shared with Thread 2, and Thread 2 calls print(). If there is no special synchronization between the write myInt = 3 of Thread 1 and the read of the same field by Thread 2, Thread 2 is not guaranteed to see the write.

    A synchronized constructor would fix this issue. Am I right about this?

    0 讨论(0)
提交回复
热议问题