18
votes

My knowledge about JVM internals is that if references are not published correctly, there is a chance that different threads will see different values of the same fields.

My question is: Does Spring beans container guarantee safe publication? If not, should I make all my bean getters and setters synchronized or use volatile? Or maybe use final fields and constructor initialization?

I assume that this may only be an issue for singleton beans as prototype beans are created on demand from requesting thread. Is my understanding correct?

1

1 Answers

13
votes

As Evgeniy has stated, the initialization of an application context happens in a single thread. Therefore the answer to your question isn't so much to do with the internals of Spring but rather the synchronization details between the thread that creates the context and the thread or threads that use the context once it has been created.

The Java memory model is based on the happens-before relation (Java Language Specification, ยง17.4.5) which is defined by various rules. For example, the call to Thread.start made in one thread to start a new thread happens-before all actions in the newly started thread itself. So if your application's main thread first creates an application context, and then starts other threads for processing, then the processing threads are guaranteed to see the fully-initialized context.

Fields marked volatile also impose a happens-before relation, in the sense that if thread A writes a value to a volatile, any other thread that sees the result of that write is also guaranteed to see anything else that thread A did before it did the volatile write. Therefore if there isn't any explicit synchronization between the initialization thread and the processing threads then the following pattern would be sufficient to ensure safety

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
  }
}