【问题标题】:Does Spring publish beans in thread-safe manner?Spring 是否以线程安全的方式发布 bean?
【发布时间】:2013-09-01 20:30:22
【问题描述】:

我对 JVM 内部的了解是,如果引用未正确发布,不同线程可能会看到相同字段的不同值。

我的问题是:Spring bean 容器是否保证安全发布? 如果没有,我应该让我所有的 bean getter 和 setter synchronized 还是使用 volatile?或者也许使用final 字段和构造函数初始化?

我认为这可能只是单例 bean 的问题,因为原型 bean 是根据请求线程的需要创建的。我的理解正确吗?

【问题讨论】:

    标签: java multithreading spring


    【解决方案1】:

    正如 Evgeniy 所说,应用程序上下文的初始化发生在单个线程中。因此,您的问题的答案与 Spring 的内部关系不大,而是创建上下文的线程与创建上下文后使用上下文的线程之间的同步细节。

    Java 内存模型基于由各种规则定义的happens-before relation(Java 语言规范,§17.4.5)。例如,在一个线程中调用Thread.start 以启动一个新线程发生在新启动线程本身的所有操作之前。所以如果你的应用的主线程先创建一个应用上下文,然后再启动其他线程进行处理,那么保证处理线程看到完全初始化的上下文。

    标记为volatile 的字段也施加了 happens-before 关系,在某种意义上,如果线程 A 将值写入volatile,则任何其他看到该写入结果的线程都是还保证看到线程 A 在进行 volatile 写入之前 所做的任何其他事情。因此,如果初始化线程和处理线程之间没有任何显式同步,那么以下模式足以确保安全

    public class Setup {
      private volatile boolean inited = false;
    
      private ApplicationContext ctx;
    
      public boolean isInited() { return inited; }
    
      public ApplicationContext getContext() { return ctx; }
    
      public void init() {
        ctx = new ClassPathXmlApplicationContext("context.xml");
        inited = true; // volatile write
      }
    }
    
    public class Processor {
      private void ensureInit() {
        while(!setup.isInited()) { // volatile read
          Thread.sleep(1000);
        }
      }
    
      public void doStuff() {
        ensureInit();
        // at this point we know the context is fully initialized
      }
    }
    

    【讨论】:

    • 没有必要。如果您对此感到担心,请将其自身的上下文标记为 volatile。
    • 这是对 JMM 的非常好的和正确的解释。但它没有回答 OP 的问题:Spring beans 容器是否保证安全发布?
    猜你喜欢
    • 1970-01-01
    • 2011-09-19
    • 1970-01-01
    • 2013-02-06
    • 1970-01-01
    • 2012-07-06
    • 1970-01-01
    • 2010-12-29
    • 1970-01-01
    相关资源
    最近更新 更多