3

考虑以下 Java 类:

class X {
    public void foo() {
        bar = 1;
    }

    protected void finalize() {
        if (bar == 1)
            baz();
    }

    private int bar = 0;
}

X.foo()在从未从任何方法(直接或间接)调用的假设下,finalize()我可以确定上面的代码没有数据竞争,也就是说,我可以确定在实际情况下X.finalize()看到写入的值叫?X.foo()X.foo()

天真的分析会说X.finalize()不能同时运行X.foo()(由于提到的假设),所以不需要额外的同步。

我猜上面的代码没有数据竞争,但令我困扰的是,语言规范在§17.4.5中包含以下明确声明,但没有说明 finalize() 和一般方法之间的发生前关系:

从对象的构造函数的末尾到该对象的终结器(第 12.6 节)的开头有一条发生前边缘。

编辑:我认为有必要使我的问题更精确,所以这里尝试对问题进行精确的重新表述:

Java 是否保证特定方法之间的发生之前的X.foo()关系,X.finalize()如果我保证X.foo()永远不会从任何finalize()方法调用(直接或间接)?在这里,happens-before将完全按照§17.4.5中的定义进行解释。

4

3 回答 3

1

无论如何,在 Java 中设置/获取 int 值是原子的。

所以我认为你不必担心。

“但对
finalize() 和一般方法之间的发生之前的关系只字未提”

那是因为这些方法(通常)不是
由 JVM 而是由您管理/调用的。所以它不能说什么。

顺便说一句,如果应用程序代码仍然可以调用 foo(),这意味着此应用程序
代码引用了您的对象,因此您的对象不符合
垃圾回收条件(即调用 finalize())。
所以,你知道,我怀疑你的担忧是否有根据。

更新:


“如果我保证永远不会从
任何 finalize() 方法(直接或间接)调用 X.foo (),Java 是否保证特定方法 X.foo() 和 X.finalize() 之间的发生前关系”

假设相反 -> 想象一下你得到的情况。

在对象 x 上调用了 finalize() 方法;然后有人在这个对象 x 上调用 foo() (注意:x 已经死了,即垃圾收集)对象。这听起来可能吗?不是我。

于 2013-12-12T13:23:55.477 回答
1

由于每个线程都有一个变量缓存,因此它不是线程安全的,在其他工作中,每个线程都有自己的变量视图。您可以应用volatine到变量以强制线程加载变量。如果从synchronized块中访问变量,则同样有效

-> java线程缓存刷新什么时候发生?

于 2013-12-12T13:29:17.887 回答
0

不,您在 foo() 和 finalize() 之间没有发生之前的关系。这基本上意味着您可能会在字段栏的 finalize() 方法中看到零。

我认为 java 内存模型基本上说每个线程都独立运行,并以未定义的顺序查看其他线程的操作,因此与平台相关。仅通过使用在实现定义的订单之前发生下所述的操作。

在您的情况下, finalize() 方法可能看不到在不同线程中运行的 foo 方法中写入的值 1 。造成这种情况的一个原因可能是,这两个线程在不同的 cpu 内核上运行,这些内核使用内存缓存。

由于这样的错误很难找到,我开发了一个名为vmlens的工具,它使用一种称为擦除器的算法来检测竞争条件

于 2013-12-15T13:33:42.913 回答