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