Java 为什么是';创建';异步的?

Java 为什么是';创建';异步的?,java,asynchronous,instance,Java,Asynchronous,Instance,我被告知,创建一个新实例总是一条异步消息;但我不明白为什么 e、 g: Foo myFoo = new Foo(); 在这里,我必须等待构造函数完成并返回我的新对象。但异步不意味着我可以独立地继续(不等待)——比如启动一个线程吗 我被告知,创建一个新实例总是一条异步消息 不,java构造函数没有隐含同步。无论如何,您可能会遇到并发问题。不能保证所有字段在构造函数调用后都已初始化 在这里,我必须等待构造函数完成并返回我的新对象。但异步不意味着我可以独立地继续(不等待)——比如启动一个线程吗 不,

我被告知,创建一个新实例总是一条异步消息;但我不明白为什么

e、 g:

Foo myFoo = new Foo();
在这里,我必须等待构造函数完成并返回我的新对象。但异步不意味着我可以独立地继续(不等待)——比如启动一个线程吗

我被告知,创建一个新实例总是一条异步消息


不,java构造函数没有隐含同步。无论如何,您可能会遇到并发问题。不能保证所有字段在构造函数调用后都已初始化

在这里,我必须等待构造函数完成并返回我的新对象。但异步不意味着我可以独立地继续(不等待)——比如启动一个线程吗

不,你不必等待。您可以在另一个线程中访问该对象

我建议你读一下这个

我被告知,创建一个新实例总是一条异步消息

对不起,我不得不说,要么你听错了,要么你被告知有什么不对。但首先,我们应该弄清楚一些术语。术语“async”或“asynchronous”表示调用立即返回给调用方。通过一个简单的实验[1],我们可以很容易地证明,对于构造函数来说这是不正确的。换句话说,构造函数必须返回调用方才能取得任何进展

启动线程确实是异步的。对
Thread.start()
的调用立即返回,并且在稍后的某个时间点,线程实际开始运行并执行
run()
方法

1.实验 考虑到您的课程(仅用于说明)如下所示:

Foo.java 如果编译并运行这个类(我在Mac上使用了Java8,但这不是必需的)。正如预期的那样,此类将永远运行,每2秒生成一次输出:

not returning yet ...
not returning yet ...
not returning yet ...
not returning yet ...
请注意,添加
sleep
调用只是为了让它可以忍受。你可以在没有它的情况下尝试这个实验,但是你的程序会通过100%的压力来压倒其中一个CPU

如果在它运行时进行了线程转储(例如,通过使用命令
jstack
),您会看到如下内容(为了简洁起见,将其缩减):


这意味着调用方(在本例中为
主线程
)将永远不会取得任何进展。由于此构造函数从不返回,因此不会发生任何进展。

当构造函数仍在执行时并发访问对象的示例:

public class Demo {

  private volatile int constructionCounter;

  private volatile String string;

  public Demo() throws InterruptedException {

    super();

    assert this.constructionCounter == 0;

    this.constructionCounter++;

    // From this point on, there's no way the constructionCounter can ever be != 1 again, because the constructor will never run more than once, concurrently or otherwise.
    assert this.constructionCounter == 1;

    final Demo newInstance = this;

    Thread t = new Thread( new Runnable() {
        public void run() {
          // Access new instance from another thread.
          // Race condition here; may print "Hello null" or "Hello World" depending on whether or not the constructor already finished.
          System.out.println("Hello " + newInstance.getString());
        }
    });

    t.start();

    this.setString( "World!" );

  }

  public String setString( String str ) {
    this.string = str;
  }

  public String getString() {
    return this.string;
  }
}

请注意,只有当构造函数本身以某种方式将
这个
交给另一个线程时,这才是可能的。

在Java中创建一个新实例是同步的。要么你被告知有错误,要么是误解。Java中的新实例是一个同步过程。您可以使用线程化进程或lambada expressionHmm获得并行进程,在UML序列图中,您通常不会为对象构造而烦恼(并且通常对象生命周期也会被忽略,除非它真的很重要)。顾名思义,序列图处理调用序列,不一定提及调用是否同步。UML本身是独立于语言的,因此也必须处理大部分异步语言(考虑Javascript,在某些情况下,您必须使用回调来保持执行顺序)。TL;DR:在序列图中,对象的创建或生命周期通常并不重要。Martin Fowler在解释序列图中的箭头时说了一句很好的话:“如果你正在阅读序列图,请注意对箭头的同步性进行假设,除非你确定作者有意进行区分。”(摘自第54页)。通常,选民会被问到为什么他们会投反对票。在这里,我想知道投票从哪里来。(我也没有投反对票,只是想知道这个问题有什么特别之处,这似乎是一个非常基本的误解……):在foo()中创建了一个B的实例-异步-;那么foo()将不必等待就可以继续吗?如果我尝试使用此对象,这将带来一些问题-比方说,我调用b.bar(),b.b的新对象不会是
null
,因此您将能够调用
b.bar()
,而不会引发
null点异常
。B可能处于半初始化状态。@fab0l1n什么给您的印象是B的实例是异步创建的?在接受答案的第二个图表中,您可以看到
foo()
仅在创建实例后在
B
上调用。两次调用之间的距离(以及A的“活动栏”的大小)既不说明两次调用之间的时间间隔,也不说明
A#foo()
在这两次调用之间做了什么。它只是表示方法上下文仍然处于活动状态,即
A#foo()
正在运行,尽管处于等待/挂起状态(它等待构造函数返回,然后调用
B#foo()
)。@Thomas闭合箭头显示同步消息,而打开箭头显示异步消息。>它等待构造函数返回,然后调用B#foo(),那么它实际上是同步的?“不,java构造函数没有暗示同步……不能保证所有字段都在构造函数调用后初始化。”-这基本上与正确的语句相反。Java构造函数总是隐式同步的:对象的构造函数不可能并发运行,即在同一个线程或另一个线程中多次运行。构造函数调用返回后,对象被完全初始化。我看不出这对OP有什么帮助。你在这里描述的这种情况还没有被问到。@Tom同意,这应该是一个注释。但该代码不会出现在注释中。我想讨论一下,如果
"main" #1 prio=5 os_prio=31 tid=0x00007f9522803000 nid=0xf07 
waiting on condition [0x000000010408f000]
   java.lang.Thread.State: TIMED_WAITING (sleeping)
    at java.lang.Thread.sleep(Native Method)
    at Foo.<init>(Foo.java:5)
    at Foo.main(Foo.java:9)
    at Foo.<init>(Foo.java:5)
    at Foo.main(Foo.java:9)
public class Demo {

  private volatile int constructionCounter;

  private volatile String string;

  public Demo() throws InterruptedException {

    super();

    assert this.constructionCounter == 0;

    this.constructionCounter++;

    // From this point on, there's no way the constructionCounter can ever be != 1 again, because the constructor will never run more than once, concurrently or otherwise.
    assert this.constructionCounter == 1;

    final Demo newInstance = this;

    Thread t = new Thread( new Runnable() {
        public void run() {
          // Access new instance from another thread.
          // Race condition here; may print "Hello null" or "Hello World" depending on whether or not the constructor already finished.
          System.out.println("Hello " + newInstance.getString());
        }
    });

    t.start();

    this.setString( "World!" );

  }

  public String setString( String str ) {
    this.string = str;
  }

  public String getString() {
    return this.string;
  }
}