我对 JVM 内部的了解是,如果引用没有正确发布,不同的线程可能会看到相同字段的不同值。
我的问题是:Spring beans 容器是否保证安全发布?如果没有,我应该让我所有的 bean getter 和 settersynchronized
还是使用volatile
?或者也许使用final
字段和构造函数初始化?
我认为这可能只是单例 bean 的问题,因为原型 bean 是根据请求线程的需求创建的。我的理解正确吗?
我对 JVM 内部的了解是,如果引用没有正确发布,不同的线程可能会看到相同字段的不同值。
我的问题是:Spring beans 容器是否保证安全发布?如果没有,我应该让我所有的 bean getter 和 settersynchronized
还是使用volatile
?或者也许使用final
字段和构造函数初始化?
我认为这可能只是单例 bean 的问题,因为原型 bean 是根据请求线程的需求创建的。我的理解正确吗?
正如 Evgeniy 所说,应用程序上下文的初始化发生在单个线程中。因此,您的问题的答案与 Spring 的内部关系不大,而是创建上下文的线程与创建上下文后使用上下文的线程或线程之间的同步细节。
Java 内存模型基于由各种规则定义的先发生关系(Java 语言规范,§17.4.5)。例如,Thread.start
在一个线程中调用以启动一个新线程发生在新启动线程本身的所有操作之前。所以如果你的应用程序的主线程首先创建一个应用程序上下文,然后启动其他线程进行处理,那么处理线程就可以保证看到完全初始化的上下文。
标记的字段volatile
还强加了一个happens-before关系,从某种意义上说,如果线程A向a写入一个值,那么volatile
任何其他看到该写入结果的线程也可以保证看到线程A在它进行易失性写入之前所做的任何其他事情. 因此,如果初始化线程和处理线程之间没有任何显式同步,那么以下模式足以确保安全
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
}
}