作者的文字内容如下:
JavaBeans模式排除了使类成为不可变的可能性,并需要程序员额外的努力来确保线程安全。
我认为作者强调的是,如果您的对象被设计为不可变的(即创建后永远不需要更改),则提供防止对象不可变性的方法是没有意义的,并且可能在线程之间创建一致性问题。
你的问题是:
为什么Java Bean模式不是线程安全的?
任何提供修改字段方式的类都不是线程安全的。这适用于JavaBeans方法(通常不使用防御性副本),但对于任何可变类也是如此。
操作不安全的类并不一定是问题,如果您将其用于没有线程之间的竞争条件的上下文中。例如,此代码是线程安全的:
Pizza pizza = new Pizza(12);
pizza.setCheese(true);
pizza.setPepperoni(true);
pizza.setBacon(true);
因为
Pizza
实例没有被声明为共享变量(实例或静态字段),而是在更受限制的范围内声明和使用(可能是方法,但也可能是初始化块)。
建造者模式提供了一种构建不可变且因此按定义是线程安全的对象的方式。
例如,通过使用构建器来创建
Pizza
:
Pizza pizza = new Pizza.Builder().cheese(true).pepperoni(true).bacon(true).build()
只有调用build()
方法才会创建并返回Pizza
对象。
之前的调用操作的是Builder
对象,并返回Builder
。
因此,如果对象是不可变的,您无需担心同步这些调用:
pizza.setCheese(true)
pizza.setPepperoni(true)
pizza.setBacon(true)
由于这些方法不需要提供,所以它们无法被调用。
关于如何拥有线程安全的JavaBeans
如果您在一个上下文中,
Pizza
实例可能会在多个线程之间共享,那么这些调用应该以同步方式进行:
pizza.setCheese(true)
pizza.setPepperoni(true)
pizza.setBacon(true)
这些方法可以声明为
synchronized
,或者
Pizza
字段可以是易失性的,但这可能不足够。
实际上,如果Pizza应根据其自身状态或甚至根据另一个对象更改其状态,我们还应该同步整个逻辑:在
Pizza
的状态修改之前进行检查。
例如,假设
Pizza
只需要添加一次Pepperoni:
代码可能是:
if (pizza.isWaitForPepperoni()){
pizza.addPepperoni(5);
}
这些语句不是原子的,因此不是线程安全的。
即使其中一个线程已经调用了
pizza.addPepperoni(5);
,“
pizza.addPepperoni(5);
”也可能被两个并发线程调用。
因此,我们应确保在不应该调用
pizza.addPepperoni(5)
时,没有其他线程调用它(比如披萨上会有过多的意大利辣肠)。例如,在
Pizza
实例上执行同步语句:
synchronized(pizza){
if (pizza.isWaitForPepperoni()){
pizza.addPepperoni(5);
}
}