17.8 Prescient Store Actions
If a variable is not declared
volatile, then the rules in the previous sections are
relaxed slightly to allow store actions to occur earlier than would otherwise be
permitted. The purpose of this relaxation is to allow optimizing Java compilers to
perform certain kinds of code rearrangement that preserve the semantics of properly synchronized programs but might be caught in the act of performing memory
actions out of order by programs that are not properly synchronized.
Suppose that a store by T of V would follow a particular assign by T of V according to the rules of the previous sections, with no intervening load or assign by T of V. Then that store action would send to the main memory the value that the assign action put into the working memory of thread T. The special rule allows the store action to instead occur before the assign action, if the following restrictions are obeyed:
- If the store action occurs, the assign is bound to occur. (Remember, these are restrictions on what actually happens, not on what a thread plans to do. No fair performing a store and then throwing an exception before the assign occurs!)
- No lock action intervenes between the relocated store and the assign.
- No load of V intervenes between the relocated store and the assign.
- No other store of V intervenes between the relocated store and the assign.
- The store action sends to the main memory the value that the assign action will put into the working memory of thread T.
This last property inspires us to call such an early store action prescient: it has to know ahead of time, somehow, what value will be stored by the assign that it should have followed. In practice, optimized compiled code will compute such values early (which is permitted if, for example, the computation has no side effects and throws no exceptions), store them early (before entering a loop, for example), and keep them in working registers for later use within the loop.