【问题标题】:Is this design of Spring singleton beans thread safe?这种 Spring 单例 bean 线程的设计是否安全?
【发布时间】:2011-09-19 03:35:12
【问题描述】:

考虑以下 Spring Service 类。定义的弹簧范围是单例。在下面的类中自动连接为字段的两个服务 bean 具有相似的结构 - 它们也由以下任一字段组成

  • Spring bean 本身
  • 无状态类
  • 不可变类

等等。这种模式在应用程序设计中普遍使用。

@Service     
public class DocumentService {  
  private final DocumentGenerationService documentGenerationService;
  private final DocumentPublishService documentPublishService;

  @Autowired
  public DocumentService (DocumentGenerationService documentGenerationService,    
                          DocumentPublishService documentPublishService) {
  this.documentGenerationService = documentGenerationService;
  this.documentPublishService = documentPublishService;
}

... methods follow

说 DocumentService 类是不可变的是否正确,因为不可能改变它的两个字段中的任何一个(它们是只能由容器本身初始化一次的 spring bean)?

无论如何,上面定义的 DocumentService bean 可以被认为是线程安全的吗?如果遵循这种设计,整个应用程序也是线程安全的吗?

【问题讨论】:

    标签: java multithreading spring concurrency singleton


    【解决方案1】:

    当 Spring 说 bean 是单例时,它不保证线程安全。如果您在 Spring 中创建单例范围的 bean,则仅表示为每个 Spring IoC 容器创建了一个对象实例。但是这个单例范围的 bean 类本身可能不是线程安全的,所以它的程序员有责任让代码线程安全。

    【讨论】:

    • 当它几乎是上述答案的副本时,为什么要添加这个答案?!似乎回答的其他人并没有使他们的评论不可变!
    【解决方案2】:

    您的代码看起来是线程安全的。 当 Spring 说 bean 是单例时,Spring 不保证线程安全。如果您在 Spring 中创建单例范围的 bean,则仅表示为每个 Spring IoC 容器创建了一个对象实例。但是这个单例范围的 bean 类本身可能不是线程安全的,所以它的程序员有责任让代码线程安全。

    在您的代码中,documentGenerationService 例如是最终的。这保证了引用不会改变,并且从新的 Java 内存模型中,也保证了实例化。但是@duffymo 说,被引用的对象也必须是不可变的。

    一切都很好:)

    【讨论】:

      【解决方案3】:

      说 DocumentService 类是不可变的是否正确,因为不可能改变它的两个字段中的任何一个(它们是只能由容器本身初始化一次的 spring bean)?

      根据definition of immutability,正式地说,这个类是NOT immutable

      如果无法更改对象的状态,则对象是不可变的,documentGenerationServicedocumentPublishService 的状态 是类状态的一部分 DocumentService

      因此,如果类具有总是相同的状态,它的行为总是相同。换句话说,没有办法改变不可变对象的行为,因为对象的行为仅取决于其状态,而在不可变对象中,此状态永远不会改变(不可变对象的示例是字符串和整数)。

      请注意,在不可变性的定义中,我们发现了一个例外:“即使某些 [...] 属性发生了变化,但对象的状态发生了变化,但对象的状态也发生了变化,但对象的状态也被认为是不可变的 [因此,行为] 似乎是不变的[...]”,但在这种情况下(根据提供的信息)引用状态的变化肯定会改变类的行为(因为我们没有对其内部状态的任何控制)。

      有一个 strategy 可以使类不可变。你已经遵循了它的一些指导方针,但如果你希望它是不可变的(我认为不是这样),你需要一些其他的,比如制作你收到的参数的 “防御性副本”在构造函数中并避免子类覆盖方法

      这个link也很有意思。

      尽管如此,你不应该让 Spring bean 不可变,因为这不是使用 Spring 提供的编程模型的方式。所以,在这种情况下,类不是不可变的,这是“好的”。

      无论如何,上面定义的 DocumentService bean 可以被认为是线程安全的吗?

      正如这里所声明的,这个类是线程安全的。许多线程可以安全地访问这个类而不会出现任何竞争条件。我们不能对它包含的字段说同样的话,但是这个类是线程安全的。这与“线程安全列表”的工作方式相同:它可以包含“无线程安全”对象,但仍然是“线程安全列表”。

      如果遵循这种设计,整个应用程序也是线程安全的?

      如果您系统的所有类都是线程安全的(即没有一个竞争条件出现在任何地方),您可以非正式地说应用程序是线程安全的。

      【讨论】:

      • @edutesoy - "... 而 documentGenerationService 和 documentPublishService 的状态是 DocumentService 类状态的一部分。" - 我认为你不能这么说明确地。这取决于你的观点。这个问题似乎是OP混乱的根源......
      • 从我的角度来看。不可变类是“总是”行为相同的类,因为它“总是”具有相同的状态。根据提供的信息,此类不是不可变的,因为 documentGenerationServicedocumentPublishService 的状态可以使 DocumentService 改变其行为,因此“这些状态是 DocumentService 状态的一部分。
      • 你只是在重复自己。从设计/建模的角度来看,GenerationService 和 PublishService 可能是 DocumentService 的一部分(...或不是)。从实现的角度来看,没有明显的理由为什么必须分析前类(为了线程安全)作为后者的组件......即使设计表明它们是。我的观点是,您正在对设计做出假设,然后根据这些假设做出教条式的陈述。
      • 我编辑了答案。它们是状态的一部分,因为您可以将任何东西作为 documentPublishService 注入,这会使类的行为变得不可预测。而且不可变类的行为应该是可预测的,因为任何类的行为都只依赖于它的状态,而在不可变类中,这个,这个状态总是一样的......
      • 注入并不一定会使它们成为一部分……或者不是。底线是线程安全实际上与使用类/对象的方式有关。 (Goetz 在他的书中对此有深刻的见解。)
      【解决方案4】:

      您问题中显示的示例代码绝对是线程安全的。

      但是,需要在整个应用程序的上下文中考虑代码。例如,上面的代码不保证documentGenerationServicedocumentPublishService 属性引用的对象的线程安全。如果它们没有充分同步,那么使用它们的代码(包括此类的其他方法)可能不是线程安全的。

      【讨论】:

        【解决方案5】:

        Spring 不保证线程安全。那是你的责任。

        所有私有成员变量都是共享的。它们可能是最终的,但这仅意味着不能更改引用。任何可变状态都必须同步。如果它们确实是不可变的,那么我认为你是在坚实的基础上。

        我同意关于自动装配依赖项的评论。如果可能的话,我会把它们留在 Spring 的控制之下。

        【讨论】:

          【解决方案6】:

          您可以将@Autowired 注解放在服务之上,而不是在构造函数中使用它们。这是一个 Spring 托管的 bean,这意味着它是一个单例。它是线程安全的,但这取决于实现。

          @Service     
          public class DocumentService {  
          
            @Autowired
            private DocumentGenerationService documentGenerationService;
          
            @Autowired
            private DocumentPublishService documentPublishService;
          
          ... methods follow
          

          【讨论】:

          • 它们不可能是 @Autowired 当前工作的方式。
          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2013-09-01
          • 1970-01-01
          • 2017-04-29
          • 1970-01-01
          • 2018-09-08
          • 2021-05-12
          相关资源
          最近更新 更多