use*_*842 40 java concurrency multithreading
我想我已经读过,字段上的final关键字保证如果线程1实例化包含该字段的对象,那么如果线程2具有对该对象的引用,则线程2将始终看到该字段的初始化值(假设它是正确建造).它还在JLS中说过
[Thread 2]还将看到那些最终字段引用的任何对象或数组的版本,这些字段至少与最终字段一样是最新的.(JLS第17.5节)
这意味着如果我有A级
class A {
private final B b = new B();
private int aNotFinal = 2;
...
Run Code Online (Sandbox Code Playgroud)
和B级
class B {
private final int bFinal = 1;
private int bNotFinal = 2;
...
Run Code Online (Sandbox Code Playgroud)
然后aNotFinal不保证在线程2获得对类A的引用时被初始化,但字段bNotFinal是,因为B是由JLS中指定的最终字段引用的对象.
我有这个权利吗?
编辑:
如果我们有两个线程同时在类C的同一个实例上执行getA(),那么可能发生这种情况的情况就是这样
class C {
private A a;
public A getA(){
if (a == null){
// Thread 1 comes in here because a is null. Thread B doesn't come in
// here because by the time it gets here, object c
// has a reference to a.
a = new A();
}
return a; // Thread 2 returns an instance of a that is not fully
// initialized because (if I understand this right) JLS
// does not guarantee that non-final fields are fully
// initialized before references get assigned
}
}
Run Code Online (Sandbox Code Playgroud)
jav*_*ter 18
你说的是真的.
将字段标记为final会强制编译器在构造函数完成之前完成字段的初始化.但是对于非最终字段没有这样的保证.这可能看起来很奇怪,但是编译器和JVM为了优化目的而做了许多事情,例如重新排序指令,导致这些事情发生.
final关键字还有很多好处.来自Java Concurecncy in Practice:
不能修改最终字段(尽管它们引用的对象如果可变则可以修改),但它们在Java内存模型下也有特殊的语义.使用final字段可以保证初始化安全性(参见第3.5.2节),它允许在不同步的情况下自由访问和共享不可变对象.
书中说:
要安全地发布对象,必须同时使对象的引用和对象的状态对其他线程可见.正确构造的对象可以通过以下方式安全发布:
- 从静态初始化程序初始化对象引用;
- 将对它的引用存储到volatile字段或AtomicReference中;
- 将对它的引用存储到正确构造的对象的最终字段中; 要么
- 将对它的引用存储到由锁正确保护的字段中.
aio*_*obe 12
我认为您的问题由JLS在第17.5.1节:最终字段的语义:
给定写入w,冻结f,动作a(不是最终字段的读取),f的最终字段的读取r 1和读取r 2使得hb(w,f),hb(f,a),mc(a,r 1)和解引用(r 1,r 2),然后当确定r 2可以看到哪些值时,我们考虑hb(w,r 2).
让我们根据这个问题细分:
bNotFinal线程1 的写入bA对象引用b(由f冻结)b.bNotFinal线程2 的一堆我们注意到了
bNotFinal在冻结之前发生bA引用在构造函数完成后发布(即冻结后)A之前读取参考A.bb访问的值b.bNotFinal以下句子......
"那么当确定r 2可以看到哪些值时,我们考虑hb(w,r 2)"
......然后翻译成
当确定读取可以看到哪些值时,
b.bNotFinal我们认为bNotFinal线程1 的写入发生在读取之前b.bNotFinal.
即线程2是保证看到价值2的b.bNotFinal.
Bill Pugh的相关引言:
能够看到字段的正确构造值是很好的,但如果字段本身是引用,那么您还希望代码查看它指向的对象(或数组)的最新值.如果您的字段是最终字段,则也可以保证.因此,您可以拥有一个指向数组的最终指针,而不必担心其他线程看到数组引用的正确值,但是数组内容的值不正确.同样,在这里"正确",我们的意思是"对象的构造函数结束时的最新",而不是"最新的可用值".
特别是这是对@supercat的一个直接答案,它提出了关于错误同步的String引用共享.