可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
According to the Java Language Specification, constructors cannot be marked synchronized because other threads cannot see the object being created until the thread creating it has finished it. This seems a bit odd, because I can indeed have another thread view the object while it's being constructed:
public class Test {
public Test() {
final Test me = this;
new Thread() {
@Override
public void run() {
// ... Reference 'me,' the object being constructed
}
}.start();
}
}
I know that this is a pretty contrived example, but it seems in theory that someone could come up with a more realistic case where marking the constructor synchronized would be legitimate in order to prevent races with threads like this one.
My question is this: is there a reason that Java would specifically disallow the synchronized modifier on a constructor? Perhaps my above example is flawed, or perhaps there really is no reason and it's an arbitrary design decision. In either case, I'm really curious and would love to know the answer.
回答1:
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.
回答2:
The question has been raised on a discussion list used by the writers of the Java concurrent API and the Java Memory Model. Several answers were given, in particular Hans Boehm replied:
Some of us (myself included IIRC) actually argued during the Java memory model deliberations that synchronized constructors should be allowed. Now I could go either way on it. Client code shouldn't use races to communicate the reference, so it shouldn't matter. But if you don't trust the clients of [your class], I think synchronized constructors could possibly be useful. And that was much of the reasoning behind final field semantics. [...] As David said, you can use synchronized blocks.
回答3:
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.
回答4:
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.
回答5:
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.
回答6:
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?
回答7:
The following code can achieve the expected result for synchronized constructor.
public class SynchronisedConstructor implements Runnable {
private int myInt;
/*synchronized*/ static {
System.out.println("Within static block");
}
public SynchronisedConstructor(){
super();
synchronized(this){
System.out.println("Within sync block in constructor");
myInt = 3;
}
}
@Override
public void run() {
print();
}
public synchronized void print() {
System.out.println(Thread.currentThread().getName());
System.out.println(myInt);
}
public static void main(String[] args) {
SynchronisedConstructor sc = new SynchronisedConstructor();
Thread t1 = new Thread(sc);
t1.setName("t1");
Thread t2 = new Thread(sc);
t2.setName("t2");
t1.start();
t2.start();
}
}
回答8:
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.
回答9:
Note that constructors cannot be synchronized — using the synchronizedkeyword with a constructor is a syntax error. Synchronizing constructors doesn't make sense, because only the thread that creates an object should have access to it while it is being constructed.