3

its a related question to this one: java: using volatile at one variable VS each variable

i have one or more different objects. i want to change some state in it and then i want to make that state visible to other threads.

For performance reasons i dont want to make each member variable in that objects volatile. And sometimes i want to use these objects in a single thread application. So in that case the volatile would be also bad.

So i had the following:

//these following mehtods change some internal variable state. These variables are not volatile or synchronized

boolean volatile memoryFlusher=false;

Thread1:

obj1->changeSomeState();
obj1->changeMoreState();
obj2->alsoSomeStateChange();

//and now i want to make that state visible to others

memoryFlusher=false; //volatile write


Thread2:

boolean tmp=memoryFlusher; // volatile read but variable is not used again

obj1->getState();
obj2->getState();

So till now its more or less the same to my related question i linked at the beginning.

So now i want to ask the following:

My memoryFlusher is not optimzed away? (unanswerd in my other question) Each volatile write/read flushes all memoryState of ALL? other (also none-volatile-)variables?

And now the really new question:

is that a good design, i do there? Cause i didnt saw any code anywhere like this, i posted here.

Should i program my Progamm in a other way? Is there a other best practise to increase the performance and getting visibilty? Is there other best practise in the program-design view, not having doing something like this?

Edit:

the state i change is unreleated. So i want with that memory-variable one memoryflusher, and no lock mechanism. It should replace a lot of single volatile variable to one. somewhere i dont need that explicit volatile memoryFlushing cause i use a synchronized, for example

synchronized(x)
{
    ...
    obj1->stateChange() //if internally is used a volatile, then i have a volatile memory flush and later the synchronized-end memory-flush, i guess (is that right?)
    ...
}

That would be one example, where it could be useful?!

summary: But is that correct, in that way i think using that memoryFlushing?

Robin Kreuzer
  • 162
  • 1
  • 10

2 Answers2

2

Your volatile reads and writes are not optimized away, but I don't see any use for this code. That's probably why you don't see it elsewhere. You didn't say what you expect from it, though, so who knows?

It looks like you want to make sure that Thread2 reliably reads the state changes made by Thread1. Well, that works only if you are sure that the code in Thread2 actually runs after the code in Thread1.

How do you know that that is the case?

I suggest that if there is really any way you can be sure that the Thread2 code happens after the Thread1 code, then there is already a memory barrier in place that establishes this fact, and renders your volatile variable unnecessary.

Matt Timmermans
  • 53,709
  • 3
  • 46
  • 87
2

My memoryFlusher is not optimzed away?

No, the write to and read from memoryFlusher will not be optimized away.

Each volatile write/read flushes all memoryState of ALL

Yes, it will synchronize memory to both threads. However, it only works if you can detect the change from one thread to another.

In your example, you read memoryFlusher, but memoryFlusher is false on initialization. How do you know Thread-1 actually wrote to it? What if, Thread-1 is at this line obj1 -> changeMoreState, and Thread-2 reads memoryFlusher. Both values would be false here and so you don't know if Thread-1 actually completed.You are out of sync as you haven't established your happens-before ordering.

To correct your code, you should write true to the memoryFlusher field and Thread-2 should only continue if memoryFlusher is true. This effectively establishes your ordering, but it also makes your code more confusing.

I'd suggest using a StampedLock instead of trying this. The StampedLock gives you really fast reads when writes are not going to be frequent.

John Vint
  • 39,695
  • 7
  • 78
  • 108
  • ok but if Thread1 changes the state again, then my signalvariable is already true; in that case i must invalidate in Thread2 the signalvariable again, right?, but where i should set the memoryFlusher again to false in Thread2?, or should i use something others than a boolean here? And why its in that way more confusing? – Robin Kreuzer Dec 17 '19 at 15:08
  • `And why its in that way more confusing?` The exact reason you just stated. Maybe confusing wasn't the right term, I can update it. It just makes the code more difficult to reason and faulty in your use case. To your point (and it is a good one), what if Thread-1 does change the value again? Well, you're out of luck and cannot establish an ordering. Your code can only be executed once. In this case, a `StampedLock` makes a lot more sense. You can use the `StampedLock` as many times as you want to safely update these fields. – John Vint Dec 17 '19 at 15:11
  • ok i thought a few minutes about all of that: if the state in the objects is not related, so i dont need to know in thread2 when thread1 changes that values. Thread2 can see that changes on that values itself. The StampedLock you mentionend is the same as the java-synchronized with internally CAS-operations, right? So in that case i get the same behaviour with StampedLocks as java-synchronized in the locking-view. But i guess there exists scenarios where i dont want to lock state (if the state is not related). Or have i now a thinking-problem?^^ – Robin Kreuzer Dec 17 '19 at 15:35