I would say that your code does not prevent the compiler from running out of order with one mutable one. This happens with several volatiles, because volatiles cannot be redirected among themselves, and volatile and normal loads / storages in some situations can be re-attached, this is one of them.
Based on the JSR 133 cookbook, the following sequence of operations can lead to a reordering of the compiler:
1st - Volatile-store/monitor-exit 2nd - Normal-load/normal-store
This sequence may allow the compiler to be reordered, so it is possible that the method might look like
void fun_by_thread_1() { this.factory = new NuclearFactory(); this.isNuclearFactory = true; }
This would bring unexpected results, as you can imagine, if fun_by_thread_1 stops after that. factory and fun_by_thread_2 start after stop.
Edit:
To answer another question. If you want to introduce ReadWriteLock, you'd better use only two volatile. Unstable reading of most processors (e.g. x86) would be less expensive, after which it would acquire read or write locks.
If only in terms of knowledge about why the read / write lock model will work. The compiler is forbidden to read or write data outside of any lock (regardless of whether it is intrinsic or jucLock). However, it can omit reading or writing in a synchronized block (for example, capping locks).
At the same time, the ReadWriteLock sequential sequence for a program with multiple threads is read and written in the sequence of the tracking sequence ReadWriteLock. What does this tell us, even if the compiler reorders the entries of your this.factory and this.isNuclearFactory (in lock methods), other threads will see them as if they were in the original (as if sequential) order.
In short, this may prevent the compiler from being reordered outside of jucLock methods, but any re-order made in locking methods will not adversely affect the overall flow of the program.