1

以下类:

class Pizza {

    Ingredients ingredients;
    Price price;

    public setIngredients(Ingredients ing) {
        if (ingredients != null) {
            throw new IllegalStateException();
        }
        ingredients = ing;
        return this;
    }

    public setPrice(Price p) {
        if (price != null) {
            throw new IllegalStateException();
        }
        price = p;
        return this;
    }

}

可以在构建器模式中使用,并且在构建后,它实际上是不可变的,因为每个属性只能设置一次。那是:

Pizza pizza = new Pizza().setIngredients(something).setPrice(somethingelse);

但是,Pizza它不是线程安全的:不能保证线程 B 看到由线程 A 设置的成分。有一些方法可以修复它:

  • 做会员final。但是你不能使用构建器模式。
  • 同步访问成员。但这似乎是浪费,因为它们只写过一次。
  • 制作它们volatile。感觉很浪费,就像同步一样。
  • 使用AtomicReference.
  • ETC。?

我的问题是,告诉 JVM 在调用某个方法后类成员不会更改的最佳方法是什么?我应该只同步对它的访问,并相信 JVM 会优化锁吗?只是感觉很浪费,因为我知道成员应该表现得像它final设置后一样。没有更好的解决方案吗?

4

2 回答 2

5

构建器模式通常意味着构建器是一个单独的对象。在这种情况下,您可以创建正在构建的对象的字段final,并在构建器对象调用的构造函数中初始化它们:

Pizza pizza = 
    new PizzaBuilder()
        .setIngredients(something)
        .setPrice(somethingelse)
        .build(); 

或者,您可以确保Pizza对象的安全发布。请注意,安全发布习惯用法适用于包含对正在发布的对象的引用的字段,而不是适用于该对象本身的字段。例如,如果pizza是某个对象的字段,您可以对其进行volatile或同步访问 - 这将确保Pizza分配给该字段的对象的安全发布。

于 2011-03-21T11:16:22.820 回答
1

如果成员值永远不会改变,更好的模式是拥有一个以andPizza为参数的构造函数,并且在对象上根本没有 setter 方法。实际上,在第一次调用后抛出异常的方法是没有用的。IngredientsPricesetSomething()

考虑String课程是如何运作的。一旦你String用一些文本实例化了 a,文本值就不能改变。获得String具有不同值的 a 的唯一方法是构造一个新的。看起来这就是你想要的。

使用这种模式还可以避免同步问题。

于 2011-03-21T11:08:48.840 回答