Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/331.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
为什么JavaBean模式不是线程安全的_Java_Constructor_Javabeans_Builder - Fatal编程技术网

为什么JavaBean模式不是线程安全的

为什么JavaBean模式不是线程安全的,java,constructor,javabeans,builder,Java,Constructor,Javabeans,Builder,Joshua Bloch在《有效Java》第二版中指出: 伸缩构造函数模式的另一种选择是JavaBean模式,在该模式中,您使用强制参数调用构造函数,然后在以下情况下调用任何可选的setter: Pizza pizza = new Pizza(12); pizza.setCheese(true); pizza.setPepperoni(true); pizza.setBacon(true); 这里的问题是,因为对象是通过多次调用创建的,所以在构建过程中,它可能处于不一致的状态。这还需要大量额外

Joshua Bloch在《有效Java》第二版中指出:

伸缩构造函数模式的另一种选择是JavaBean模式,在该模式中,您使用强制参数调用构造函数,然后在以下情况下调用任何可选的setter:

Pizza pizza = new Pizza(12);
pizza.setCheese(true);
pizza.setPepperoni(true);
pizza.setBacon(true);
这里的问题是,因为对象是通过多次调用创建的,所以在构建过程中,它可能处于不一致的状态。这还需要大量额外的工作来确保线程安全

我的问题:- 以上代码不是线程安全的吗?我错过了什么基本的东西吗

提前感谢,


苏里亚

作者从文本上说:

JavaBeans模式排除了使类不可变的可能性,并且需要程序员进行额外的工作 确保螺纹安全。

我认为作者强调了这样一个事实,即提供防止对象不可变的方法是没有意义的,如果对象被设计为不可变的,则可能会在线程之间产生一致性问题:也就是说,对象一旦创建就不需要更改。

你的问题:

为什么JavaBean模式不是线程安全的

任何提供字段变异方法的类都不是线程安全的。
JavaBeans方法(通常不使用防御性拷贝)也是如此,但任何可变类也是如此。

如果在线程之间没有争用条件的上下文中使用非线程安全类,那么操纵该类就不是问题。
例如,此代码是线程安全的:

Pizza pizza = new Pizza(12);
pizza.setCheese(true);
pizza.setPepperoni(true);
pizza.setBacon(true);
因为
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
字段可能是易变的,但这还不够。

事实上,如果一个比萨饼应该根据它自己的状态或者甚至根据另一个对象改变它的状态,我们也应该同步整个逻辑:进行检查,直到
比萨饼的状态修改

例如,假设
比萨饼
只需添加一些单位的意大利香肠一次:

代码可以是:

  if (pizza.isWaitForPepperoni()){
      pizza.addPepperoni(5);
  }
这些语句不是原子语句,因此没有线程安全性。

pizza.add辣香肠(5)可以被两个并发线程调用,即使其中一个线程已经调用了
pizza.addPepperoni(5)

因此,我们应该确保没有其他线程调用pizza.addPepperoni(5)
,而它不应该调用(pizza会有太多的Pepperoni)。
例如,在
Pizza
实例上执行同步语句:

   synchronized(pizza){
      if (pizza.isWaitForPepperoni()){
          pizza.addPepperoni(5);
      }
   }

您向我们展示的代码只涉及一个线程,所以此代码的线程安全性是没有意义的

如果多个线程可以看到
Pizza
实例,那么有几件事需要担心:

  • 在您完成初始化之前,其他线程是否可以看到
    Pizza
    实例

  • 当另一个线程看到该实例时,它是否会观察属性的正确值

  • 第一个问题是,在完成初始化之前,不要“发布”对另一个线程的引用

    第二个问题可以通过使用适当的同步机制来解决,以确保更改是可见的。这可以通过多种方式实现。例如:

    public class Pizza {
         private boolean cheese;
    
         public synchronized /* added */ void setCheese(boolean cheese) {
             this.cheese = cheese;
         }
    
         public synchronized /* added */ boolean isCheese() {
             return cheese;
         }
    }
    
    • 您可以将getter和setter声明为
      synchronized
      方法
    • 您可以将保存属性值的(私有)变量声明为
      volatile
    注意,JavaBean模式没有规定如何构造bean。在您的示例中,使用无参数构造函数,然后使用setter设置字段。您还可以实现一个构造函数,它允许您传递参数,为属性提供(非默认)初始值

    这还需要大量额外的工作来确保线程安全

    不是真的。在这种情况下,使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的基础知识!!!调用Pizza的每个新实例都将创建一个新实例。因此,没有通过多个线程共享。为什么您认为上面的代码不是线程安全的?我的理解是,我们将在方法中创建新实例(比如方法createPizza())。当多个线程调用createPizza()方法时,它们有自己的Pizza实例,并且是线程安全的。是吗?是的,你的理解是正确的!!那么,我的陈述与书中的内容相矛盾,对吗?它说了什么?也许你误解了?我不认为他是在问这个。他不是在问如何使上面的代码线程安全。@Minh Kieu你是对的。我编辑以设置上下文。“Pizza Pizza=new Pizza.Builder().cheese(true).Pearoni(true).bacon(true).build();”这不是一个调用。这是5个调用。@Angel O'Sphere只有对
    build()
    的调用才能创建并返回Pizza对象