如何重现代码规范中提到的多线程错误?(TSM03-J.不要发布部分初始化的对象)



我正在使用谷歌翻译阅读"SEI CERT Oracle Coding Standard for Java"。

其中一个建议是 TSM03-J.不要发布部分初始化的对象, 它给出了将导致"发布部分初始化的对象"的代码

我想重现文章中提到的错误,但我失败了。有人可以给我示例代码吗?

不要与以下方案混淆,尽管它也可以获取部分初始化的对象。 TSM01-J.在对象构造过程中不要让此引用转义

class Foo {
private Helper helper;
public Helper getHelper() {
return helper;
}
public void initialize() {
helper = new Helper(42);
}
}
public class Helper {
private int n;
public Helper(int n) {
this.n = n;
}
// ...
}

然后该页面上有一个解释

如果线程在执行 initialize(( 方法之前使用 getHelper(( 方法访问帮助程序,则该线程将观察到未初始化的帮助程序字段。稍后,如果一个线程调用 initialize(( 而另一个线程调用 getHelper((,则第二个线程可能会观察到以下情况之一:

  • 帮助程序引用为 null
  • n 字段设置为 42 的完全初始化的帮助程序对象
  • 具有未初始化 n 的部分初始化帮助程序对象,其中包含默认值 0

出于测试目的,我将以下代码添加到"帮助程序"中

public int getN() {
return n;
}

以下是测试代码,我以为测试代码会输出"发布部分初始化的对象",但没有任何反应。

public class Program {
public static void main(String[] args) {
boolean getZero = false;
while (!getZero) {
Foo foo = new Foo();
new Thread(foo::initialize).start();
while (true) {
Helper helper;
if ((helper = foo.getHelper()) != null) {
if (helper.getN() == 0) {
getZero = true;
System.out.println("publish partially initialized objects");
}
break;
}
}
}
}
}

如何重现代码规范中提到的多线程错误?

来自同一页面,

特别是,JMM 允许编译器为新的帮助程序对象分配内存,并在初始化新的帮助程序对象之前将该内存的引用分配给帮助程序字段。换句话说,编译器可以对帮助程序实例字段的写入和初始化帮助程序对象的写入(即 this.n = n(进行重新排序,以便前者首先发生。这可能会公开一个竞争窗口,在此期间,其他线程可以观察部分初始化的帮助程序对象实例。

要在此处观察部分初始化的对象,您的代码需要在读取 foo.getHelper((.getN(( 时点击此竞赛窗口。你可以运行它数百万次,但仍然没有发生这种情况。像大多数与多线程相关的错误一样,它不容易复制。

最新更新