tem*_*def 63 java constructor language-design synchronized
根据Java语言规范,构造函数不能被标记为同步,因为在创建它的线程完成之前,其他线程无法看到正在创建的对象.这看起来有点奇怪,因为在构造对象时我确实可以有另一个线程查看对象:
public class Test {
public Test() {
final Test me = this;
new Thread() {
@Override
public void run() {
// ... Reference 'me,' the object being constructed
}
}.start();
}
}
Run Code Online (Sandbox Code Playgroud)
我知道这是一个非常人为的例子,但理论上似乎有人可以提出一个更现实的案例,标记构造函数同步将是合法的,以防止像这样的线程的比赛.
我的问题是:Java有没有理由不在构造函数上禁用synchronized修饰符?也许我上面的例子是有缺陷的,或者也许没有理由,这是一个任意的设计决定.在任何一种情况下,我都很好奇,很想知道答案.
Paŭ*_*ann 30
如果你真的需要同步其余构造函数与任何线程无论如何获得对你尚未完全构造的对象的引用,你可以使用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
}
}
}
Run Code Online (Sandbox Code Playgroud)
通常,将"尚未构造"的对象"放弃"为"坏"样式是不合适的,因此不需要同步构造函数.
在某些极端情况下,同步构造函数会很有用.这是一个更现实的例子,来自对Bozho答案的讨论:
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.");
}
}
}
Run Code Online (Sandbox Code Playgroud)
我们希望doSomethingDangerous()
只在我们的SubClass对象构造完成后调用该方法,例如我们只想要"一切正常"输出.但在这种情况下,当您只能编辑SubClass时,您就没有机会实现这一目标.如果构造函数可以同步,它将解决问题.
所以,我们从中学到了什么:如果你的类不是final的话,就不要像我在超类构造函数中所做的那样做 - 并且不要从构造函数中调用自己类的任何非final方法.
ass*_*ias 17
在Java并发API和Java内存模型的编写者使用的讨论列表中提出了这个问题.给出了几个答案,特别是Hans Boehm回答说:
我们中的一些人(我自己包括IIRC)在Java内存模型审议期间实际上认为应该允许同步构造函数.现在我可以选择任何一种方式.客户端代码不应该使用种族来传播引用,因此它无关紧要.但如果你不相信[你的班级]的客户,我认为同步构造函数可能是有用的.这是最终字段语义背后的大部分原因.[...]正如大卫所说,你可以使用同步块.
因为synchronized
保证多个线程不执行对相同对象的操作.当调用构造函数时,您仍然没有该对象.两个线程在逻辑上不可能访问同一对象的构造函数.
在您的示例中,即使新线程调用了一个方法,它也不再是构造函数 - 它是关于正在同步的目标方法.
在您的示例中,构造函数实际上只从一个线程调用一次.
是的,可以获得对不完整构造的Object的引用(关于双重检查锁定的一些讨论以及为什么它被破坏揭示了这个问题),但是,不是通过第二次调用构造函数.
在构造函数上同步将阻止两个线程同时在同一个Object上调用构造函数,这是不可能的,因为永远不可能在对象实例上调用构造函数两次.
JLS中的“ 构造器修饰符”部分明确指出
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.
Run Code Online (Sandbox Code Playgroud)
因此,不需要同步构造函数。
另外,不建议在创建对象之前给出对象引用(this)。一种可能的模棱两可的情况是,在创建子类对象时给出对象引用是超类构造函数。