Joshua Bloch 在 Effective Java,第 2 版中指出:
伸缩构造函数模式的另一种选择是 JavaBean 模式,您可以在其中调用带有强制参数的构造函数,然后在之后调用任何可选的 setter :
Pizza pizza = new Pizza(12);
pizza.setCheese(true);
pizza.setPepperoni(true);
pizza.setBacon(true);
这里的问题是,因为对象是通过多次调用创建的,它可能在构建过程中处于不一致状态。这也需要付出很多额外的努力来确保线程安全。
我的问题:- 上面的代码不是线程安全的吗?我是否缺少任何基本的东西?
提前致谢
苏里亚
最佳答案
您向我们展示的代码仅涉及一个线程,因此该代码的线程安全没有实际意义。
如果多个线程可以看到Pizza
实例,那么有两件事需要担心:
在您完成初始化之前,另一个线程能否看到
Pizza
实例?当另一个线程看到实例时,它会观察到正确的属性值吗?
第一个问题是在完成初始化之前不“发布”对另一个线程的引用。
第二个问题可以通过使用适当的同步机制来解决,以确保更改可见。这可以通过多种方式完成。例如:
- 您可以将 getter 和 setter 声明为
同步
方法。 - 您可以将保存属性值的(私有(private))变量声明为
volatile
。
请注意,JavaBean 模式并未规定 bean 的构造方式。在您的示例中,您使用无参数构造函数,然后使用 setter 设置字段。您还可以实现一个构造函数,它允许您传递为属性提供(非默认)初始值的参数。
This also requires a lot of extra effort to ensure thread safety
不是真的。在这种情况下,使 getter 和 setter 线程安全是一个小改动。例如:
public class Pizza {
private boolean cheese;
public synchronized /* added */ void setCheese(boolean cheese) {
this.cheese = cheese;
}
public synchronized /* added */ boolean isCheese() {
return cheese;
}
}
关于java - 为什么 Java Bean 模式不是线程安全的,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/44345780/