实例初始化和赋值到共享变量的重新排序可能吗? [英] Is reordering of instance initialization and assignment to a shared variable possible?

查看:139
本文介绍了实例初始化和赋值到共享变量的重新排序可能吗?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我在阅读一篇文章,其实际上谈论了双重检查锁定,但我惊讶于一个更基本的失败代码作为示例。这里指出,实例的初始化(即,在构造函数返回之前发生的对实例变量的写入)可以在之后被重新排序。之后,对实例的引用被写入共享

I was reading an essay, which actually talks about double-checked locking, but I'm surprised about an even more basic failure in the code presented as examples. It is stated there that it is possible that the initialization of an instances (i.e. writes to the instance variables that happen before the constructor returns) may be reordered to after a reference to the instance is written to a shared variable (a static field in the following example).

是否存在以下类 Foo 的定义, ,其中一个线程执行 Foo.initFoo(); 和一个不同的线程执行 System.out.println(Foo.foo.a) code>,第二个线程可以打印 0 (而不是 1 > NullPointerException )?

Is it true that with the following definition of class Foo, with one thread executing Foo.initFoo(); and a different thread executing System.out.println(Foo.foo.a);, the second thread may print 0 (instead of 1 or throwing a NullPointerException)?

class Foo {
    public int a = 1;

    public static Foo foo;

    public static void initFoo() {
        foo = new Foo();
    }

    public static void thread1() {
        initFoo(); // Executed on one thread.
    }

    public static void thread2() {
        System.out.println(foo.a); // Executed on a different thread
    }
}

关于Java内存模型(和其他语言的内存模型),它实际上并不让我感到惊讶,这是可能的,但直觉投票非常强烈,因为它是不可能的(也许因为对象初始化涉及,对象初始化看起来在Java中如此神圣) 。

From what I know about the Java memory model (and memory models in other languages) it actually doesn't surprise me that this is possible but intuition is voting very strongly for it being impossible (maybe because object initialization is involved and object initialization seems so sacred in Java).

可以修复这个代码(即它永远不会打印 0 第一个线程

Is it possible to "fix" this code (i.e. that it will never print 0) without synchronization in the first thread?

推荐答案

调用 foo = new Foo();

A call to foo = new Foo(); involves several operations which might be reordered unless you introduce proper synchronization to prevent it:


  1. 为新对象分配内存

如果没有正确的同步,步骤3和4可能被重新排序(注意步骤2必然发生在步骤4之前),尽管在x86架构上不太可能发生热点。

Without proper synchronization, steps 3 and 4 might be reordered (note that step 2 necessarily happens before step 4), although it is unlikely to happen with hotspot on a x86 architecture.

为了防止它,你有几个解决方案,例如:

To prevent it you have several solutions, for example:


  • make init init )同步访问 foo c $ c> AND getter)。

  • make a final
  • synchronize access to foo (with a synchronized init AND getter).

无需进入复杂的JLS# 17,您可以阅读 JLS#12.4.1 关于类初始化(强调我):

Without going into the intricacies of JLS #17, you can read JLS #12.4.1 about class initialization (emphasis mine):


初始化代码不受限制的事实允许示例构造其中当类变量在其初始化表达式求值之前仍具有其初始默认值时,可以观察其类型,但这种示例在实践中很少见。 (这样的示例也可以构造为例如变量初始化。)Java编程语言的全部功能在这些初始化器中可用;程序员必须小心谨慎。这个功能给代码生成器增加了额外的负担,但是这种负担在任何情况下都会发生,因为Java编程语言是并发的。

The fact that initialization code is unrestricted allows examples to be constructed where the value of a class variable can be observed when it still has its initial default value, before its initializing expression is evaluated, but such examples are rare in practice. (Such examples can be also constructed for instance variable initialization.) The full power of the Java programming language is available in these initializers; programmers must exercise some care. This power places an extra burden on code generators, but this burden would arise in any case because the Java programming language is concurrent.

这篇关于实例初始化和赋值到共享变量的重新排序可能吗?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆