提问者:小点点

这不是用Java解释Final的坏例子吗?


Java 规范 17.5 具有以下代码来说明 Java 内存模型中最终字段的使用。(与普通字段相比)

class FinalFieldExample { 
    final int x;
    int y; 
    static FinalFieldExample f;

    public FinalFieldExample() {
        x = 3; 
        y = 4; 
    } 

    static void writer() {
        f = new FinalFieldExample();
    } 

    static void reader() {
        if (f != null) {
            int i = f.x;  // guaranteed to see 3  
            int j = f.y;  // could see 0
        } 
    } 
}

规范接着说:

“类 FinalFieldExample 有一个最终的 int 字段 x 和一个非最终的 int 字段 y。一个线程可能执行方法编写器,另一个线程可能执行方法读取器。由于编写器方法在对象的构造函数完成后写入 f,因此可以保证读取器方法看到 f.x 的正确初始化值:它将读取值 3。但是,f.y 不是最终的;因此,不能保证读取器方法看到它的值 4。

我的问题是:这难道不是一个蹩脚的(或者至少是一个拙劣的)例子吗?或者我在这里错过了什么?

我把这个例子称为“跛脚”的理由是:

如果FinalFieldExample类的对象要在多线程场景中由线程共享,那么它是否应该遵循多线程的基本原则,即使用某种形式的同步。如果他们使用了同步,那么上述问题将不存在。

上面的例子似乎提倡最终字段作为正确同步技术的替代方案(或部分安抚器)。在我的理解中,最终字段即使在正确同步的基础上使用也有用。并且永远不应该被用来获得示例中提到的优势(在没有同步的情况下)。

所以有人会问:难道没有一个像样的例子(同步)来解释最终场相对于普通场的优势吗?我想,不可变是!


共3个答案

匿名用户

您混淆了<code>同步</code>和并发。

如果字段是常量,则可以在多个线程之间安全地共享该字段,而无需锁定。

如果字段是变量,则需要同步或以其他方式锁定。

您可以有一个并发程序,该程序具有多个线程读取相同的常量字段,这不会阻止任何线程

任何使用< code>synchronized块的代码都要付出巨大的代价。这是一个非常昂贵的过程,应该尽可能避免。更不用说资源饥饿、死锁、活锁等问题了。等等...

如果可以使用final而不是synchronized,那么应该这样做。

匿名用户

编辑:我错过了这个回答的重点。问题不在于价值可以改变。改为看bmorris591的回答。

不可变对象的优点之一是您不需要同步。

但是这个例子不是关于同步,而是关于保证读者线程看到的值。即使有了同步,< code>y的值也可能改变,而< code>x的值总是保证为3。

匿名用户

你参考的这个规范只是描述了这些东西的行为。基于此规范,您可以决定如何正确编码。这个例子绝不试图代表真实的用例。它只是用几行文字说明了行为是什么。如果您的jvm实现没有这样的行为,那么它就是一个bug。