2

Consider the following code:

Thread A:
closed = true; // closed is a volatile variable
close();

Thread B:
while(true){
  if(!closed){
    write();
  }
  else {
    ...
  }
}

If I understand correctly, closed = true; happens-before close();, and closed = true; happens-before write();, but it seems that there is no happens-before relationship between close(); and write();.

Does the above code ensures that write(); will only get called before close()? If it does not, what modification can be done to make it work? Or do I have to use synchronized?

ZumiKua
  • 506
  • 4
  • 10
  • You need synchronization. Volatile doesn't have any locking. It simply make sure that the write happens in the memory atomically. – Mangat Rai Modi Jul 16 '19 at 08:17
  • Synchronized make sure that no other thread goes in the same block where a thread is already in. This doesn't seem to help here. You have to use locking. – Mangat Rai Modi Jul 16 '19 at 08:19
  • volatile does not make sure that write happens atomically. Look here: https://stackoverflow.com/questions/19744508/volatile-vs-atomic. If you declare a variable as volatile, it means that variable will always be read from the main memory and not cached. And yes, you need locking. Have a look at here: https://stackoverflow.com/questions/289434/how-to-make-a-java-thread-wait-for-another-threads-output – humbaba Jul 16 '19 at 08:24
  • what does `close()` do ? – diginoise Jul 16 '19 at 10:59
  • 1
    @diginoise it calls `quit()` on an Android HandlerThread, which is not related to this question, I think? – ZumiKua Jul 16 '19 at 11:06

3 Answers3

1

You already gave the anser: "Or do I have to use synchronized?" Yes!

arneh
  • 11
  • 1
1

Basically what you need here is an order, i.e. write statement executed before close().

  1. Volatile will not help, as it just writes the variable directly into memory. It dictates nothing for the close() statement
  2. Synchronized keyword simply prevents another thread from going into another/same synchronized block, synchronized over the same monitor. Again not a good fit for ordering, if both tasks have to be done by a different thread.

A very simple example of solving that in Kotlin (java will be similar). You create an object which can be used for locking.

  1. Your closing thread A will wait to acquire the lock on Object lock
  2. Another thread B will not wait to get the lock. instead, it will close and notify all the threads to do their job.
  3. A now can enter the critical section, close() and release the lock again.

val lock = Object()

val A = Thread{        
   synchronized(lock) {
       lock.wait()
       closed = true; // closed is a volatile variable
       close();
       lock.notifyAll()
   }
}

val B = Thread{
   synchronized(lock) {
       write()
       lock.notifyAll()
   }
}

t.start()
t1.start()

t.join()
t1.join()

Note that this method is highly flawed and a basic example to put you on track. You might want to use a reentrant lock and think it through with your complete code.

Mangat Rai Modi
  • 5,397
  • 8
  • 45
  • 75
  • Thanks for your answer, but it seems that you misunderstood my question, I don't need `write()` to be invoked *exactly* before `close()`, I just want to ensure that no `write()` will be invoked after `close()`, I have modified the code to make it more clear. – ZumiKua Jul 16 '19 at 10:51
  • @ZumiKua I didn't get time to edit my answer. If your sole worry is to prevent write after close has happened. Then you don't need to use explicit locking. Your assumption is correct but you may end up refusing some writes when close set to true, but the actual close() action hasn't happened yet. Simply have two synchronized function. One to close and set variable, and the other to write. This is way neater imho. I will edit my answer later. – Mangat Rai Modi Jul 16 '19 at 15:17
1

You cannot guarantee that close() happens before write().

You have volatile closed as semaphore of sorts. You can use that to signal events, but you should not use it to guarantee mutual exclusion. In your case Thread A can signal to Thread B (A is writing to, B reading from volatile variable) but Thread A cannot receive a signal from Thread B which leads to a Problematic Situation described below.

volatile only guarantees that reads will see the most recent writes but does not guarantee that your write will happen before your read.

The Problematic Sutuation:

Thread B could be in the middle of write() while Thread A flips closed = true and proceeds to execute close().

Use explicit locking by protecting entire execution of write() and use the same lock to ensure that you are not calling close() while writing is happening. You can do that by synchronizing on the same object as per Mangat's answer or use ReadWriteLock from SDK.

diginoise
  • 7,352
  • 2
  • 31
  • 39