Is it possible to prevent out-of-order execution by using single volatile
By referring article, it is using a pair of volatile
to prevent out-of-order execution. I was wondering, is it possible to prevent it using single volatile
?
void fun_by_thread_1() {
this.isNuclearFactory = true;
this.factory = new NuclearFactory();
}
void fun_by_thread_2() {
Factory _factory = this.factory;
if (this.isNuclearFactory) {
// Do not operate nuclear factory!!!
return;
}
// If out-of-order execution happens, _factory might
// be NuclearFactory instance.
_factory.operate();
}
Factory factory = new FoodFactory();
volatile boolean isNuclearFactory = false;
The reason I ask, is because I have a single guard flag (similar to isNuclearFactory
flag), to guard开发者_Python百科 against multiple variables (similar to many Factory
). I do not wish to mark all the Factory
as volatile.
Or, shall I fall into the following solution?
void fun_by_thread_1() {
writer.lock();
try {
this.isNuclearFactory = true;
this.factory = new NuclearFactory();
} finally {
writer.unlock();
}
}
void fun_by_thread_2() {
reader.lock();
try {
Factory _factory = this.factory;
if (this.isNuclearFactory) {
// Do not operate nuclear factory!!!
return;
}
} finally {
reader.unlock();
}
_factory.operate();
}
Factory factory = new FoodFactory();
boolean isNuclearFactory = false;
P/S: I know instanceof
. Factory is just an example to demonstrate of out-of-order problem.
Your first solution has the problem that if factory
is not volatile, there is no visibility guarantee. That is, if it is changed in one thread, other threads may not see the changed value the same time as they see the changed isNuclearFactory
value, or may not even see it at all.
So it is possible that
- Thread A calls
fun_by_thread_1()
- Thread B calls
fun_by_thread_2()
and it sees thatisNuclearFactory == true
, however still sees the previous value offactory
, which may benull
, or may refer to a non-nuclear factory.
Luckily, the fix to this particular problem is easy: reverse the order of assignments in fun_by_thread_1()
.
void fun_by_thread_1() {
this.factory = new NuclearFactory();
this.isNuclearFactory = true;
}
Changing a volatile value is propagated to all other threads; moreover, it also guarantees the visibility of all changes made by the current thread prior to touching the volatile variable.
However, this introduces a new data race problem: now it is possible that
- Thread B calls
fun_by_thread_2()
and gets a reference tofactory
, which happens to point to a non-nuclear factory - Thread A calls
fun_by_thread_1()
- Thread B tests
isNuclearFactory
and sees that it istrue
(as set by Thread A), so it returns without using_factory
although it refers to a non nuclear factory!
And it is also possible that
- Thread A calls
fun_by_thread_1()
and creates a new nuclear factory - Thread B calls
fun_by_thread_2()
and gets a reference tofactory
, which happens to point to the new nuclear factory - Thread B tests
isNuclearFactory
and sees that it is stillfalse
, so it operates the nuclear_factory
!
So my answer to your title question is No. In fact, even with two volatile
variables you can easily have issues. Whenever you have two distinct, but logically related variables exposed to multiple threads, the best solution is to encapsulate them into a single object, thus you can update both values at once, by changing the single (volatile
) reference.
I would argue your code does not prevent out of order compiler execution with a single volatile. It does with multiple volatiles because volatiles cannot be re-ordered amongst one another but volatile and normal loads/stores can in certain situations be re-oredered, this is one of them.
Based on the JSR 133 cookbook the operation sequence as followed can result in a compiler re-ordering:
1st - Volatile-store/monitor-exit
2nd - Normal-load/normal-store
This sequence can allow for compiler re-ordering so it is possible that the method can look like
void fun_by_thread_1() {
this.factory = new NuclearFactory();
this.isNuclearFactory = true;
}
This would have unexpected results as you can imagine if fun_by_thread_1 halts after this.factory and fun_by_thread_2 begins after said halt.
Edit:
To answer your other question. If you wanted to introduce a ReadWriteLock you are better off just using two volatiles. The volatile reads on most processors (x86 for example) would be less costly then acquires either read or write locks.
If just for a knowledge point of view of why the reader/writer lock model would work. The compiler is not allowed to hoist reads or writes outside of any lock (whether intrinsic or j.u.c.Lock). It can however lower the read or write within a synchronized block (called lock coarsening).
That being said, the reads and writes within the ReadWriteLock gauntness sequential consistency for the program with multiple threads. What that tells us is even if the compiler will re-order the writes of your this.factory
and this.isNuclearFactory
(within the lock methods) other threads will see them as if they were in the original (as if serial) order.
In short it may prevent compiler re-orderings outside of the j.u.c.Lock methods but any re-ordering done in the lock methods will not negatively effect the overall program flow.
I get a pretty solid reference article
http://www.ibm.com/developerworks/library/j-jtp03304/
Under the new memory model, when thread A writes to a volatile variable V, and thread B reads from V, any variable values that were visible to A at the time that V was written are guaranteed now to be visible to B
To thread 1, before volatile isNuclearFactory
set to true, factory
will always be FoodFactory
. The above statement should be applied to thread 2 too, as any variable values that were visible to thread 1 at the time that isNuclearFactory
was written are guaranteed now to be visible to B.
Regardless of the attribute visibility issues, I don't think you can make this work without a locking mechanism. In your simple example, even if the out of order problem was solved, thread 1 can be preempted after setting isNuclearFactory
to true
. In that case, thread 2 will miss the execution of a food factory.
精彩评论