Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/399.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 关于并发性,final关键字究竟保证了什么?_Java_Multithreading_Concurrency - Fatal编程技术网

Java 关于并发性,final关键字究竟保证了什么?

Java 关于并发性,final关键字究竟保证了什么?,java,multithreading,concurrency,Java,Multithreading,Concurrency,我想我已经读到,字段上的final关键字保证,如果线程1实例化包含该字段的对象,那么如果线程2引用了该对象(前提是该对象构造正确),那么线程2将始终看到该字段的初始化值。JLS中还指出 [Thread 2]还将看到所引用的任何对象或数组的版本 这些最终字段至少与最终字段一样是最新的 是的 这意味着如果我有A级 class A { private final B b = new B(); private int aNotFinal = 2; ... B类 class B { pr

我想我已经读到,字段上的final关键字保证,如果线程1实例化包含该字段的对象,那么如果线程2引用了该对象(前提是该对象构造正确),那么线程2将始终看到该字段的初始化值。JLS中还指出

[Thread 2]还将看到所引用的任何对象或数组的版本 这些最终字段至少与最终字段一样是最新的 是的

这意味着如果我有A级

class A {
  private final B b = new B();
  private int aNotFinal = 2;
  ...
B类

class B {
  private final int bFinal = 1;
  private int bNotFinal = 2;
  ...
然后,不能保证在线程2获取对类a的引用时初始化aNotFinal,但字段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
  }
}

你说的是真的

将字段标记为final将强制编译器在构造函数完成之前完成字段的初始化。但是,对于非最终字段,没有此类保证。这可能看起来很奇怪,但是编译器和JVM为了优化的目的做了很多事情,比如重新排序指令,导致了这些事情的发生

最后一个关键字还有很多好处。从Java Concurrency的实践来看:

不能修改最终字段(尽管它们引用的对象可以修改,如果它们是可变的),但是 在Java内存模型下也有特殊的语义。使用final字段使保证成为可能 初始化安全性(见第3.5.2节),允许自由访问和共享不可变对象,而无需 同步

书上说:

要安全地发布对象,对对象的引用和对象的状态都必须对其他用户可见 线程同时运行。正确构造的对象可以通过以下方式安全发布:

  • 从静态初始值设定项初始化对象引用
  • 将对它的引用存储到易失性字段或原子引用中
  • 将对它的引用存储到正确构造的对象的最终字段中
  • 将对它的引用存储到由锁正确保护的字段中
上述行仅保证当您从A的实例访问b时,b将被初始化。现在,b的初始化细节或b的任何实例化完全取决于b的定义方式,在这种情况下,它可能根据JLS进行初始化,也可能不进行初始化


所以,如果你做A=newa();从一个线程读取a.b,并以某种方式从另一个线程读取a.b,可以保证,如果a不为null,您不会看到null,但b.bNotFinal可能仍然为零。

值得一提的是,
final
在线程的值可见性方面与
volatile
具有相同的用途。也就是说,不能在字段上同时使用
final
volatile
,因为它们彼此冗余。回到你的问题上来。正如其他人指出的那样,您的假设是错误的,因为JLS只保证对B的引用的可见性,而不是B中定义的非最终字段。但是,您可以使B按照您希望的方式进行行为。一种解决方案是将
bNotFinal
声明为
volatile
,如果它不能是
final

,我想JLS会在您引用的部分下方回答您的问题,在:

给定一个写W,一个冻结F,一个动作A(这不是一个最终字段的读取),一个由F冻结的最终字段的读R1,和一个读R2,例如Hb(w,f),Hb(f,a),MC(a,r1),和去数(R1,R2),然后当确定哪些值可以被R2看到时,我们考虑Hb(w,r2)。 让我们从问题的角度来细分:

  • w:是线程1对
    bNotFinal
    的写入吗
  • f:是冻结
    b
  • a:发布
    a
    对象引用
  • r1:线程2读取
    b
    (被f冻结)
  • r2:线程2产生的
    b.bNotFinal
    区域
我们注意到

  • hb(w,f):写入
    bNotFinal
    发生在
    b
  • hb(f,a)
    a
    参考在构造函数完成后(即冻结后)发布
  • mc(a,r1):线程2在读取
    a.b
  • 解引用(r1,r2):线程2通过访问
    b.bNotFinal
下面这句话

当确定R2能看到哪些值时,我们考虑Hb(w,r2)

…然后翻译成

当确定<代码> B.BNOTATECTON/COD>的读数可以看到哪些值时,我们认为线程1中的写到<代码> BNoTrime<代码>发生在代码< B.BNEXTATECTON/CODE> >

之前。 也就是说,保证螺纹2可以看到
b.bNotFinal
的值
2


比尔·普格:

查看字段正确构造的值的能力很好,但是如果字段本身是引用,那么您还希望代码能够查看它所指向的对象(或数组)的最新值。如果您的字段是最终字段,这也是有保证的。因此,您可以拥有指向数组的最终指针,而不必担心其他线程看到数组引用的正确值,但数组内容的值不正确。同样,这里的“正确”是指“截至对象构造函数结束时的最新值”,而不是“可用的最新值”

特别是,这是对@supercat示例的直接回答,该示例涉及
String
引用的不同步共享。
class A {
    private final B b = new B();
}