I'm coding an enum strategy pattern where one of the strategies makes use of an ScheduledExecutor:
class ControllerImpl { //... boolean applyStrat(StratParam param) { getStrat().apply(param); } //... private enum Strats { NOOP (NoopStrategyImpl.class), REAL (RealtimeStrategyImpl.class), SCHED (ScheduledStrategyImpl.class), ; private final Strategy strategy; Mode(Class<? extends Strategy> strategyClass) { this.strategy = getInjector().getInstance(strategyClass); } public boolean applyStrategy(StratParam param) { return this.strategy.apply(param); } } class ScheduledStrategyImpl implements Strategy { private ScheduledExecutor executor = Executors.new..().schedule..(); @Override public boolean apply(StratParam param) { // actual apply() logic } } The goal of doing it like this, is to be able to hot-swap strategies. The default strategy is REAL, but during runtime it may be switched to NOOP or SCHED.
Now, the above code initializes the executor during static initialization of the enum. This means that SCHED will contain a running executor -- even if it is never used.
But I don't want to start the executor unless the strategy is actually used (no point in having it running in the background for no reason).
The common solution for this is to lazily initialize the executor in the apply() method instead:
class ExecutorStrategyImpl implements Strategy { private ScheduledExecutor executor = null; @Override public boolean apply(..) { if (executor == null) { executor = Executors.new..().schedule..(); } // actual apply() logic } } This way, the executor will get initialized the first time the strategy is applied, not sooner. And if SCHED is never used, the executor is never created.
However, I don't want to waste cycles checking for (executor == null) every single time SCHED is apply()ed, when I know the condition will always be false except for the first time.
To avoind doing so, this is what I came up with:
class ExecutorStrategyImpl implements Strategy { private ScheduledExecutor executor = null; private Function<StratParam, Boolean> normal = param -> { // normal apply() logic }; private Function<StratParam, Boolean> firstTime = param -> { innerStrategy = normal; if (executor == null) { executor = Executors.new..().schedule..(); } return normal.apply(param); } private volatile Function<StratParam, Boolean> innerStrategy = firstTime; @Override public boolean apply(StratParam param) { return innerStrategy.apply(param); } } The reasoning behind the above code is as follows:
innerStrategyinitially points tofirstTimelambda, which includes the executor initialization code.- The first thread to get to
apply()invokesinnerStrategy.apply()and thusfirstTimefunctionality. innerStrategyis changed tonormal;volatilekeyword ensures that this change will be visible for every other thread that comes after.- Executor is initialized.
- Every other thread calling
innerStrategy.apply()getsnormalfunctionality instead.
My question is: is there a window between 2. and 3. where two threads may read innerStrategy with the same firstTime value, thus creating two executors? Or is volatile enough to prevent that?
And, of course: is there a better way to do this?
applymethod is called?ifis negligible, but... If there's a way to eliminate both, I'd like to try it.