Is it possible to prevent execution of an extraordinary execution using a single volatile

When sending an article, it uses a volatile pair to prevent out-of-turn execution. I was wondering if it is possible to prevent its use of 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 one security flag (similar to the isNuclearFactory flag) to protect against multiple variables (similar to many Factory ). I do not want to mark all Factory as mutable.

Or can I get into the next 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 of demonstrating a problem out of order.

+4
source share
4 answers

In your first solution, the problem arises that if the factory not mutable, there is no guarantee of visibility. That is, if it changes in one thread, other threads may not see the changed value at the same time as they see the changed isNuclearFactory value, or they may not even see it at all.

Thus, it is possible that

  • Topic A calls fun_by_thread_1()
  • Thread B calls fun_by_thread_2() and it sees that isNuclearFactory == true , however it still sees the previous factory value, which may be null , or may refer to a non-nuclear factory.

Fortunately, fixing this particular task is easy: reverse the assignment order in fun_by_thread_1() .

 void fun_by_thread_1() { this.factory = new NuclearFactory(); this.isNuclearFactory = true; } 

Changing a volatile value applies to all other threads; in addition, it also ensures that all changes made by the current thread are visible until the variable is touched.

However, this introduces a new problem with the calculation of data: it is now possible that

  • Thread B calls fun_by_thread_2() and gets a factory reference, which seems to point to a non-nuclear factory
  • Topic A calls fun_by_thread_1()
  • Tests Thread B isNuclearFactory and sees that it is true (as specified by Thread A), so it returns without using _factory , although it refers to a non-nuclear factory!

And it’s also possible that

  • Thread A calls fun_by_thread_1() and creates a new fun_by_thread_1() factory
  • Thread B calls fun_by_thread_2() and gets a link to the factory , which points to a new nuclear factory
  • Tests Thread B isNuclearFactory and sees that it is still false , so it controls nuclear _factory !

So my answer to your title question is: No. In fact, even with two volatile variables, you can easily have problems. Whenever you have two different but logically related variables that are exposed to multiple threads, the best solution is to encapsulate them in one object, so you can update both values ​​at the same time by changing a single ( volatile ) link.

+4
source

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.

+1
source

I get a pretty solid help article

http://www.ibm.com/developerworks/library/j-jtp03304/

In the new memory model, when thread A writes a variable V and thread B reads from V, any variable values ​​that were visible to A at the time that V was written are now guaranteed to be visible to B

In thread 1, before volatile isNuclearFactory set to true, the factory will always be FoodFactory . The above statement should also apply to stream 2, since any variable values ​​that were visible in stream 1 during isNuclearFactory writing isNuclearFactory now guaranteed to be visible to B.

+1
source

Regardless of the problems of attribute visibility, I do not think that you can do this work without a locking mechanism. In your simple example, even if the problem was not resolved isNuclearFactory , thread 1 can be unloaded after setting isNuclearFactory to true . In this case, thread 2 will skip the factory power execution.

0
source

Source: https://habr.com/ru/post/1337799/


All Articles