挥发性出版物的保证深度如何? [英] How deep volatile publication guarantees?

查看:99
本文介绍了挥发性出版物的保证深度如何?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

众所周知,如果我们有一些对象引用并且此引用具有final字段-我们将看到final字段中的所有可访问字段(至少在构造函数完成时)

As is known guarant that if we have some object reference and this reference has final field - we will see all reachable fields from final field(at least when constructor was finished)

class Foo{
    private final Map map;
     Foo(){
         map = new HashMap();
         map.put(1,"object");
     }

     public void bar(){
       System.out.println(map.get(1));
     }
}

正如我在这种情况下所理解的那样,我们保证bar()方法总是输出object,因为: 1.我列出了类Foo的完整代码,并且map是最终的;
2.如果某个线程看到Foo的引用,而该引用!= null,则我们保证从最终map引用值可达到的值将是实际的.

As I undertand at this case we have guarantee that bar() method always output object because:
1. I listed full code of class Foo and map is final;
2. If some thread will see reference of Foo and this reference != null, then we have guarantees that reachable from final map reference value will be actual.

我也认为

class Foo {
    private final Map map;
    private Map nonFinalMap;

    Foo() {
        nonFinalMap = new HashMap();
        nonFinalMap.put(2, "ololo");
        map = new HashMap();
        map.put(1, "object");
    }

    public void bar() {
        System.out.println(map.get(1));
    }

    public void bar2() {
        System.out.println(nonFinalMap.get(2));
    }
}

这里我们对bar()方法有相同的保证,但是bar2可以抛出NullPointerException,尽管nonFinalMap赋值在map赋值之前发生.

here we have same gurantees about bar() method but bar2 can throw NullPointerException despite nonFinalMap assignment occurs before map assignment.

我想知道挥发物:

class Foo{
        private volatile Map map;
         Foo(){
             map = new HashMap();
             map.put(1,"object");
         }

         public void bar(){
           System.out.println(map.get(1));
         }
    }

据我了解,bar()方法不能抛出NullPoinerException,但是可以打印null. (我对此完全不确定)

As I understand bar() method cannot throw NullPoinerException but it can print null; (I am fully not sure about this aspect)

class Foo {
    private volatile Map map;
    private Map nonVolatileMap;

    Foo() {
        nonVolatileMap= new HashMap();
        nonVolatileMap.put(2, "ololo");
        map = new HashMap();
        map.put(1, "object");
    }

    public void bar() {
        System.out.println(map.get(1));
    }

    public void bar2() {
        System.out.println(nonFinalMap.get(2));
    }
}

我认为这里我们对bar()方法也有同样的保证,bar2()也不能抛出NullPointerException,因为nonVolatileMap赋值写的是更高的volatile映射赋值,但它可以输出null

I think here we have same gurantees about bar() method also bar2() cannot throw NullPointerException because nonVolatileMap assignment written higher volatile map assignment but it can output null

添加,在Elliott Frisch评论之后

Added after Elliott Frisch comment

通过种族发布示例:

public class Main {
    private static Foo foo;

    public static void main(String[] args) {
        new Thread(new Runnable() {
            @Override
            public void run() {
                foo = new Foo();
            }
        }).start();


        new Thread(new Runnable() {
            @Override
            public void run() {
                while (foo == null) ; // empty loop

                foo.bar();
            }
        }).start();

    }
}

请批准或更正我对代码段的注释.

Please proove or correct my comments to code snippets.

推荐答案

在当前Java内存模型的领域中,volatile不等于final.换句话说,您不能将final替换为 ,并认为安全施工保证是相同的.值得注意的是,这在理论上可能会发生:

In the realm of current Java Memory Model, volatile does not equal final. In other words, you cannot replace final with volatile, and think the safe construction guarantees are the same. Notably, this can theoretically happen:

public class M {
  volatile int x;
  M(int v) { this.x = v; }
  int x() { return x; }
}

// thread 1
m = new M(42);

// thread 2
M lm;
while ((lm = m) == null); // wait for it
print(lm.x()); // allowed to print "0"

因此,在构造函数中写入volatile字段并不安全.

So, writing the volatile field in constructor is not as safe.

直觉:在上面的示例中,m上有一场比赛.通过使 field M.x volatile不能消除种族,只有使m本身为volatile会有所帮助.换句话说,该示例中的volatile修饰符在错误的地方有用.在安全发布中,您必须具有写入->易失性写入->易失性读取,可观察到易失性写入->读取(现在先观察易失性写入之前的写入)",而必须具有易失性写入->写入->读取- >易失性读取(不会观察到易失性写入)".

Intuition: there is a race on m in the example above. That race is not eliminated by making the field M.x volatile, only making the m itself volatile would help. In other words, volatile modifier in that example is at the wrong place to be useful. In safe publication, you have to have "writes -> volatile write -> volatile read that observes volatile write -> reads (now observing writes prior the volatile write)", and instead you have "volatile write -> write -> read -> volatile read (that does not observe the volatile write)".

琐事1:此属性表示我们可以在构造函数中更加积极地优化volatile-.这证实了可以放松未观察到的易失性存储的直觉(实际上,只有在具有不转义的this结束符的构造函数之前,它才能被观察到).

Trivia 1: This property means we can optimize volatile-s much more aggressively in constructors. This corroborates the intuition that unobserved volatile store (and indeed it is not observed until constructor with non-escaping this finishes) can be relaxed.

琐事2:这也意味着您不能安全地初始化volatile变量.在上面的示例中,将M替换为AtomicInteger,您将具有独特的现实生活行为!在一个线程中调用new AtomicInteger(42),不安全地发布实例,然后在另一个线程中执行get() -您是否保证遵守42?如前所述,JMM说不". Java内存模型的最新修订版努力确保所有初始化的安全构造,以捕获这种情况.还有许多重要的非x86端口为了安全起见,已经对此进行了强化.

Trivia 2: This also means you cannot safely initialize volatile variables. Replace M with AtomicInteger in the example above, and you have a peculiar real-life behavior! Call new AtomicInteger(42) in one thread, publish the instance unsafely, and do get() in another thread -- are you guaranteed to observe 42? JMM, as stated, says "nope". Newer revisions of Java Memory Model strive to guarantee safe construction for all initializations, to capture this case. And many non-x86 ports where that matters have already strengthened this to be safe.

琐事3: Doug Lea :此finalvolatile问题导致java.util.concurrent中的某些曲折结构允许在自然不存在的情况下允许0作为基数/默认值.该规则很糟糕,应该更改."

Trivia 3: Doug Lea: "This final vs volatile issue has led to some twisty constructions in java.util.concurrent to allow 0 as the base/default value in cases where it would not naturally be. This rule sucks and should be changed."

也就是说,该示例可以变得更狡猾:

That said, the example can be made more cunning:

public class C {
  int v;
  C(int v) { this.x = v; }
  int x() { return x; }    
}

public class M {
  volatile C c;
  M(int v) { this.c = new C(v); }
  int x() { 
    while (c == null); // wait!
    return c.x();
  }
}

// thread 1
m = new M(42);

// thread 2
M lm;
while ((lm = m) == null); // wait for it
print(lm.x()); // always prints "42"

如果在volatile读取观察到volatile写入构造函数中写入的值之后,通过volatile字段进行了传递式读取,则通常会采用安全发布规则.

If there is a transitive read through volatile field after volatile read observed the value written by volatile write in constructor, the usual safe publication rules kick in.

这篇关于挥发性出版物的保证深度如何?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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