gpt4 book ai didi

java - 对象构造后读取字段陈旧值

转载 作者:塔克拉玛干 更新时间:2023-11-03 03:02:22 25 4
gpt4 key购买 nike

我正在阅读 Brian Goetz 所著的“Java 并发实践”一书。第 3.5 和 3.5.1 段包含我无法理解的语句。

考虑以下代码:

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。

因此,根据文本,如果时机不巧,value = 0 是可能的;并且在下一刻值 = 42。

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

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

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),以强制线程在对象完全构造之前等待。

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. 新线程(新初始化()).start();完整构建Holder对象耗时3秒
  2. 新线程(新检查()).start();由于 Holder 对象仍未构造代码将抛出 NullPointerException

因此,无法模拟字段具有默认值的情况。

我的问题:

  1. 作者对这个并发问题的理解有误吗?
  2. 或者无法模拟字段默认值的行为?

最佳答案

我尝试用下面的代码来测试这个问题。

测试:

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 的程序。我不认为这能证明什么,也没有运行超过几分钟,但我希望这将是一个设计良好的测试的更好起点,或者至少有助于找出测试中的一些可能缺陷。

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

我还担心检查值是否为 null 或捕获 NullPoiterException 可能会过多影响计时。

请注意,目前接受的答案是 Improper publication of Java Object Reference说这个问题在 x86 架构下可能是不可能的。它也可能依赖于 JVM。

关于java - 对象构造后读取字段陈旧值,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50655856/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com