Java 规范17.5有以下代码来说明 Java 内存模型中 final 字段的使用。(与普通字段相比)
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。一个线程可能执行方法 writer,另一个线程可能执行方法 reader。因为 writer 方法在对象的构造函数完成后写入 f,reader 方法将保证看到 fx 的正确初始化值:它将读取值 3。但是,fy 不是最终的;因此不能保证读取器方法看到它的值 4。
我的问题是:这不是一个蹩脚的(或至少是人为的)例子吗? 还是我在这里遗漏了什么?
我将这个例子称为“蹩脚”的理由是:
如果FinalFieldExample 类的对象要在多线程场景中被线程共享,它不应该遵循多线程的基本原则,即使用某种形式的同步。如果他们使用了同步,那么上面提到的问题就不会存在。
上面的示例似乎提倡将 Final 字段作为适当同步技术的替代方案(或部分安慰者)。据我了解,即使在正确同步的基础上使用 final 字段也是有用的。并且永远不应该用于获得示例中提到的优势(在没有同步的情况下)。
所以有人可能会问:难道没有一个像样的例子(带有同步)来解释 final 字段相对于普通字段的优势吗?我想,不变性是!