构造对象后读取字段过时的值 [英] Read field stale value after object construction

查看:101
本文介绍了构造对象后读取字段过时的值的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在阅读Brian Goetz所著的"Java并发实践"一书. 3.5和3.5.1段包含了我无法理解的陈述.

I'm reading a book "Java concurrency in practice" by Brian Goetz. Paragraphs 3.5 and 3.5.1 contains statements that I can not understand.

考虑以下代码:

public class Holder {
  private int value;
    public Holder(int value) { 
    this.value = value;
  }

  public void assertValue() {
    if (value != value) throw new AssertionError("Magic");
  }
}

class HolderContainer {
  // Unsafe publication
  public Holder holder;

  public void init() {
    holder = new Holder(42);  
  }
}

作者声明:

  1. 在Java中,对象构造函数在子类构造函数运行之前首先将默认值写入所有字段.
  2. 因此,可以将字段默认值视为过时的值.
  3. 线程在第一次读取字段时可能会看到过时的值,然后在下一次读取时会看到最新的值,这就是assertN会抛出AssertionError的原因.

因此,根据本文,在一些不幸的时机下,值可能为0;否则,可能为0.并在下一个值= 42.

So, according to the text, with some unlucky timing it is possible that value = 0; and in the next moment value = 42.

我同意第1点,即对象构造函数首先使用默认值填充字段.但我不明白第2点和第2点. 3.

I agree with point 1 that Object constructor firstly fills fields with default values. But I don't understand points 2 & 3.

让我们更新作者代码并考虑以下示例:

Let's update authors code and consider the following example:

public class Holder {
  int value;

  public Holder(int value) {
    //Sleep to prevent constructor to finish too early
    try {
     Thread.sleep(3000);
    } catch (InterruptedException e) {
     e.printStackTrace();
    }
    this.value = value;
  }

  public void assertValue()  {
    if(value != value) System.out.println("Magic");
  }
}

我添加了Thread.sleep(3000),以强制线程在对象完全构建之前等待.

I've added Thread.sleep(3000), to force thread to wait before object will be fully constructed.

public class Tests {

  private HolderContainer hc = new HolderContainer();

  class Initialization implements Runnable {
    public void run() {
      hc.init();
    }
  }

  class Checking implements Runnable {
    public void run() {
      hc.holder.assertValue();
    }
  }

  public void run() {
    new Thread(new Initialization()).start();
    new Thread(new Checking()).start();
  }
}

例如:

  1. 第一个线程在其持有者对象中
  2. 第二个线程调用assertValue

主线程运行两个线程:

  1. new Thread(new Initialization()).start();完全构建Holder对象需要3秒
  2. 新线程(new Checking()).start();由于Holder对象仍未构造,因此代码将抛出 NullPointerException

因此,当字段具有默认值时,无法模拟情况.

Therefore, it's impossible to emulate situation when field has default value.

我的问题:

  1. 作者对此并发问题有误吗?
  2. 还是无法为字段默认值模拟行为?

推荐答案

我尝试使用以下代码测试问题.

I tried to test the problem with the following code.

测试:

public class Test {
    public static boolean flag =true;
    public static HolderContainer hc=new HolderContainer();

    public static void main (String args[]){    
        new Thread(new Initialization()).start();
        new Thread(new Checking()).start();
    }
}

class Initialization implements Runnable {
    public void run() {
        while (Test.flag){
            Test.hc=new HolderContainer();
            Test.hc.init();
            }
    }
}

class Checking implements Runnable {
    public void run() {
        try{
            Test.hc.holder.assertValue();
        }
        catch (NullPointerException e) {
        }    
    }
}

持有人:

public class Holder {
    private int value;
        public Holder(int value) { 
        this.value = value;
    }

    public void assertValue() {
        if (value != value) {
            System.out.println("Magic");
            Test.flag=false;
        }
    }
}

class HolderContainer {
    public Holder holder;
    public void init() {
        holder = new Holder(42);  
    }
}

我从没有程序可以将value!=value评估为true. 我认为这并不能证明任何事情,并且不会运行超过两分钟,但是我希望这将是设计良好的测试的一个更好的起点,或者至少有助于找出测试中可能存在的缺陷.

I never got the program to evaluate value!=value to true. I don't think this proves anything and didn't run it for more than a couple minutes, but I hope this will be a better starting point for a well designed test or at least help to figure out some possible flaws in the tests.

我试图在Test.hc=new HolderContainer();Test.hc.init();之间,public Holder holder;public void init() {之间以及public void init() {之后插入睡眠.

I tried to insert a sleep between Test.hc=new HolderContainer(); and Test.hc.init();, between public Holder holder; and public void init() { and after public void init() {.

我还担心检查值是否为null或捕获NullPoiterException可能会严重影响计时.

I am also concerned that checking if a value is null or catching the NullPoiterException may affect the timing too much.

请注意,当前对 Java Object Reference的不当发布的答案表示,在x86下,该问题可能是不可能的建筑学.它也可能取决于JVM.

Please note that the currently accepted answer to Improper publication of Java Object Reference says this problem is probably impossible under an x86 architecture. It may also be JVM dependent.

这篇关于构造对象后读取字段过时的值的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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