我刚刚在学习this了解新 JMM 中最终字段的行为(5 及以上)。这个概念很明确:在正确构造对象后,保证初始化的最终字段对所有线程的可见性。
但在本节的最后,我读到了这一点,这让我很困惑:
现在,说了所有这些,如果在一个线程构造一个不可变对象(即仅包含最终字段的对象)之后,您想要确保它被所有其他线程正确地看到,您通常仍然需要使用同步。例如,没有其他方法可以确保第二个线程可以看到对不可变对象的引用。
这是否意味着虽然各个最终字段(组成不可变对象)不存在同步(例如,此处的可见性)问题。但是,首次在线程中创建的不可变对象本身可能在其他线程中不可见(正确创建)?
如果是这样,虽然我们可以跨线程共享初始化的不可变对象,而不必担心线程不安全,但在创建时,它们需要像其他可变对象一样“特别注意”线程安全吗?
Final 字段的语义,定义于JLS 第 17.5 条,保证:
仅在对象完全初始化后才能看到对该对象的引用的线程保证能看到该对象的最终字段的正确初始化值。
换句话说,它说if一个线程sees一个完全初始化的对象,then它保证看到它的最终字段已正确初始化。
但是,不能保证该对象对给定线程可见。这是一个不同的问题。
如果您不使用某种同步来发布对象的引用,那么其他线程可能永远无法看到对该对象的引用。
考虑以下代码:
final class A {
private final int x;
A(int x) { this.x = x; }
public getX() { return x; }
}
class Main {
static volatile A a1 = null;
static A a2 = null;
public static void main(String[] args) {
new Thread(new Runnable() { void run() { try {
while (a1 == null) Thread.sleep(50);
System.out.println(a1.getX()); } catch (Throwable t) {}
}}).start()
new Thread(new Runnable() { void run() { try {
while (a2 == null) Thread.sleep(50);
System.out.println(a2.getX()); } catch (Throwable t) {}
}}).start()
a1 = new A(1); a2 = new A(1);
}
}
请注意,a1
场是不稳定的。这确保最终,对该字段的写入将对稍后读取该字段的所有线程可见。场a2
不是易失性的(因此,一个线程对此字段的写入可能永远不会被其他线程注意到)。
在这段代码中,我们可以确定线程 1 将完成执行(也就是说,它将看到a1 != null
。但是,线程 2 可能会停止,因为它永远不会看到对该字段的写入a2
,因为它不是易失性的。
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)