1

Those who have developed professional, multi-threaded, Java Spring applications can probably testify the use of the volatile keyword is almost non-existent (and other threading controls for that matter), despite the potential disastrous consequences of missing it when needed.

Let me provide an example of very common code

@Service
public class FeatureFlagHolder {
   private boolean featureFlagActivated = false;

   public void activateFeatureFlag() {
      featureFlagActivated = true;
   }

   // similar code to de-activate

   public boolean isFeatureFlagActivated() {
      return featureFlagActivated;
   }
}

Suppose the threads changing and reading the state of featureFlagActivated are different. The thread reading the boolean could, AFAIK, according to the JVM cache its value and never refresh it. In practice, I've never seen that happen. Actually, I've never even seen the boolean not being updated immediately on a read.

Why is that?

edu
  • 428
  • 2
  • 10
  • There could be some magic inside spring which ensures the visibility by other means. – Andy Turner Feb 24 '22 at 09:39
  • 1
    "I've never seen that happen" the important point is not whether you've seen it: it's whether it is provably impossible (up to the guarantees of the spec). Code can appear to work just fine, maybe for years, until one day it suddenly doesn't, because something _apparently unrelated_ has changed. – Andy Turner Feb 24 '22 at 09:40
  • 1
    Probably related: https://stackoverflow.com/questions/23906808/should-i-mark-object-attributes-as-volatile-if-i-init-them-in-postconstruct-in – Andy Turner Feb 24 '22 at 09:52
  • 2
    There's frequently a pattern where certain beans straight up just belong to a single thread or a single request, so whatever variables are toggled, no external party will see that. And many applications also don't hold variables in service components. After all, services are *stateless*, as indicated by Spring's guidelines. Your example implementation is thus an exhbition of an anti-pattern. – M. Prokhorov Feb 24 '22 at 09:58
  • Does this answer your question? [Should I mark object attributes as volatile if I init them in @PostConstruct in Spring Framework?](https://stackoverflow.com/questions/23906808/should-i-mark-object-attributes-as-volatile-if-i-init-them-in-postconstruct-in) – pringi Feb 24 '22 at 11:03

1 Answers1

5

At the most basic level it has to be said that a lack of volatile doesn't guarantee that it will fail. It just means that the JVM is allowed to do optimizations that could lead to failure. But whether those optimizations happen and whether they then lead to failure is influenced by many different factors. Therefore it's often very hard to actually detect these problems, until they become catastrophic.

For a starter, I'd like to summarize conditions that happen to frequently coincide when it does go wrong.

  • the non-volatile variable is usually read in a tight loop
  • the non-volatile variable is changed rarely, but when it changes it's "important" in some sense.
  • the amount of code executed inside that loop is small (roughly small enough to be fully inlined by an aggressive compiler)
  • the tight loop over-running has a very visible effect (for example it leads to an exception and not just silently doing unnecessary work).

Note that not all of those are necessary, but they tend to be true when I actually observe the issue.

My personal interpretation (plus some reading on the topic) lead me to these rules of thumb:

  • if reading the wrong value won't be noticed, then you simply won't notice if the volatile is missing. If the only bad thing that happens is that you run through a loop a couple of times unnecessarily, then chances are you will never realize that it happens.
  • when the reads of the volatile variable happen with enough "distance" between them (where distance is measured by other read access to other parts of memory) then it can often behave as if it was volatile, simply because it drops out of the cache
  • any kind of synchronization on anything inside the loops tends to have the effect of invalidating some caches at least and thus causes the variable to act as if it was volatile.

These three alone make it rather hard to actually spot the problem except in very extreme cases (i.e. when executing once too many causes a big crash in your system).

In your specific example, I assume that the feature flag is not something that will be toggled multiple times per second. It's more likely that it's set once per process and then stays untouched.

For example, if you have multiple incoming requests in the same second and halfway through the second you toggle the feature flag it can happen that some of the requests that happen after the toggling will still use the old value, due to having it cached from earlier.

Will you notice? Unlikely. It'll be extremely hard to distinguish "this request came in just before the change" from "this request came in just after the change and wrongly used the old value". If 6 out of 10 requests use the old value instead of the correct 5 out of 10, there's a good chance no one will ever notice.

Joachim Sauer
  • 302,674
  • 57
  • 556
  • 614