Java 中的对象初始化是原子的吗?
Is object initialization atomic in Java?
我知道 myObject1 = myObject2
是原子的,但下面这行代码是原子的吗?
Object obj1 = new Object();
真的可以在初始化引用的过程中挂起吗?
它不是原子的。没有什么可以阻止您将 sleep
放入 Object 构造函数、无限循环或在构造函数中抛出异常。
注意:虽然可能,但这些都是anti-patterns,不推荐。
这一行不是原子的。发生两件事:首先,创建新的 Object
(通常不是原子的),然后将对该对象的引用分配给变量 obj1
。比方说,Thread1
和 Thread2
执行的代码行观察了随机时刻变量的状态。 Thread2
可以观察到三种状态(为简单起见,假设对象创建本身是原子的),
obj1 == null
,未创建对象
obj1 == null
,对象已创建,但对它的引用尚未分配给变量
obj1 != null
,行完全执行
原子性表明 Thread2
可能只观察状态 1 和 3,但必须强制执行。使其成为原子的一种方法是通过同步(在此示例中 obj1
是一个 class 字段):
public synchronized Object getObj() {
if (obj1 == null)
obj1 = new Object();
return obj1;
}
调用此方法的第一个线程将初始化该字段,以后的调用将简单地 return 变量。由于互斥,一个线程不可能在对象创建后在obj1
中观察到null
,因为不同线程的方法调用不能重叠。
引用分配是原子的。在任何情况下都不会部分分配引用本身(这显然会导致 VM 有时崩溃)。它将为空或 not-null。在分配引用之前对象的构造不是原子的,但在构造过程完成之前,您将无法访问要分配给引用的新对象。
有趣的是,引用分配可能发生在与主内存不同步的芯片缓存内存中。这意味着其他线程不会立即看到分配。有两种方法可以确保引用分配被推送到主内存,其他线程可以立即看到它。 (1) 将引用声明为 volatile 并且 (2) 将引用赋值放在同步块中(退出同步块会将缓存的引用提交到主内存)
我知道 myObject1 = myObject2
是原子的,但下面这行代码是原子的吗?
Object obj1 = new Object();
真的可以在初始化引用的过程中挂起吗?
它不是原子的。没有什么可以阻止您将 sleep
放入 Object 构造函数、无限循环或在构造函数中抛出异常。
注意:虽然可能,但这些都是anti-patterns,不推荐。
这一行不是原子的。发生两件事:首先,创建新的 Object
(通常不是原子的),然后将对该对象的引用分配给变量 obj1
。比方说,Thread1
和 Thread2
执行的代码行观察了随机时刻变量的状态。 Thread2
可以观察到三种状态(为简单起见,假设对象创建本身是原子的),
obj1 == null
,未创建对象obj1 == null
,对象已创建,但对它的引用尚未分配给变量obj1 != null
,行完全执行
原子性表明 Thread2
可能只观察状态 1 和 3,但必须强制执行。使其成为原子的一种方法是通过同步(在此示例中 obj1
是一个 class 字段):
public synchronized Object getObj() {
if (obj1 == null)
obj1 = new Object();
return obj1;
}
调用此方法的第一个线程将初始化该字段,以后的调用将简单地 return 变量。由于互斥,一个线程不可能在对象创建后在obj1
中观察到null
,因为不同线程的方法调用不能重叠。
引用分配是原子的。在任何情况下都不会部分分配引用本身(这显然会导致 VM 有时崩溃)。它将为空或 not-null。在分配引用之前对象的构造不是原子的,但在构造过程完成之前,您将无法访问要分配给引用的新对象。
有趣的是,引用分配可能发生在与主内存不同步的芯片缓存内存中。这意味着其他线程不会立即看到分配。有两种方法可以确保引用分配被推送到主内存,其他线程可以立即看到它。 (1) 将引用声明为 volatile 并且 (2) 将引用赋值放在同步块中(退出同步块会将缓存的引用提交到主内存)