hap*_*s10 3 java concurrency multithreading final immutability
我正在研究这个,以了解新JMM中的最终字段的行为(5以后).这个概念很清楚:在正确构造对象之后,保证初始化的最终字段对所有线程的可见性.
但是在本节的最后,我读到了这个,这让我感到困惑:
现在,说完所有这些,如果在一个线程构造一个不可变对象(即一个只包含最终字段的对象)之后,你想确保所有其他线程都能正确看到它,你通常还需要使用同步.例如,没有其他方法可以确保第二个线程可以看到对不可变对象的引用.
这是否意味着虽然单个最终字段(组成不可变对象)没有同步(例如,此处可见性)问题.但是,首次在线程中创建的不可变对象本身在其他线程中可能不可见(正确创建)?
如果是这样,虽然我们可以跨线程共享初始化的不可变对象而没有任何线程不安全的担忧,但在创建时,他们需要"特别关注"线程安全,就像其他mutable一样?
JLS第17.5节中定义的最终字段的语义保证:
在该对象完全初始化之后只能看到对象引用的线程可以保证看到该对象的最终字段的正确初始化值.
换句话说,它表示如果一个线程看到一个完全初始化的对象,那么它可以保证看到它的最终字段被正确初始化.
但是,无法保证对象对给定线程可见.这是一个不同的问题.
如果您不使用某种同步来发布对象的引用,那么另一个线程可能永远无法看到对它的引用.
请考虑以下代码:
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);
}
}
Run Code Online (Sandbox Code Playgroud)
请注意,该a1字段是易变的.这样可以确保最终对该字段的写入对于稍后读取它的所有线程都是可见的.该字段a2不是volatile(因此,一个线程对该字段的写入可能永远不会被其他线程注意到).
在这段代码中,我们可以确定线程1将完成执行(也就是说,它会看到它a1 != null.但是,线程2可能会停止,因为它永远不会看到对字段的写入a2,因为它不是易失性的.