java - 使用构建器/工厂模式确保内存可见性

标签 java thread-safety volatile synchronized memory-visibility

下面的类:

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 设置的配料。有一些方法可以解决这个问题:

  • 使成员最终。但是你不能使用构建器模式。
  • 同步对成员的访问。但这似乎是浪费,因为它们只写一次。
  • 让它们易变。感觉浪费,就像同步一样。
  • 使用AtomicReference
  • 等等?

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

最佳答案

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

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

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

关于java - 使用构建器/工厂模式确保内存可见性,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/5376691/

相关文章:

c++ - 在没有 typedef 的情况下使用时显示编译错误的函数的 volatile 指针;需要帮助 w/"void (* volatile userFunc)(void)"

java - Volatile 和 ArrayBlockingQueue 以及其他可能的并发对象

java - 当字段的值取决于其先前值时,为什么 Volatile 不起作用

java - 当我在 java 中有纬度和经度时,如何获取位置的地址?

java - 将 Javascript 代码插入脚本元素以设置窗口栏

java - groovy 属性类的 JAXB 注释

c# - ReaderWriterLockSlim 阻塞读取,直到所有排队的写入完成

Java 文档字符串通过类中的私有(private)变量

java - Java类中的线程安全

c++ - boost C++ 库 : Unit Test Assertion on Deadlocks