1
class OrderingTest {
   var x = 0
   var y = 0
   fun test() {
       thread {
           x = 1
           y = 1
       }
       thread {
           val a = y
           val b = x
           println("$a, $b")
       }
   }
}


I had this piece of code to explains the compiler re-ordering and race conditions. It had all possible outcomes:

1,0
0,1
0,0
1,1

But now considering this code

class OrderingTest {
   var x = 0
   @Volatile var y = 0
   fun test() {
       thread {
           x = 1
           y = 1
       }
       thread {
           val a = y
           val b = x
           println("$a, $b")
       }
   }
}


Can someone explain how does this adding volatile keyword to y makes 1,0 case impossible?

cheems
  • 164
  • 7

1 Answers1

3

Forget about Kotlin for a bit, you have to go down to the JVM. What you are asking is about the memory model of the JVM and something that is called happens before.

The very short answer is that when a variable has the volatile modifier, it triggers a happens before relation. This ensures that everything that happened up to that point on the thread will be visible to other threads.

In your example, because y is marked as volatile, whenever the value of y changes, both the values of x and y will be visible to the other thread.

I would recommend you to go a bit through the rabbit hole and read about the semantics of happens before (there are some videos on youtube about this too). I also recommend reading the excellent book Java Concurrency In Practice. Knowing this is crucial if you are going to be writing multithreaded software. Unfortunately most developers don't know about this (or safe publishing) and introduce all kinds of really hard to find bugs, so share what your find with your team!

Augusto
  • 28,839
  • 5
  • 58
  • 88
  • Thanks. Will definitely read the book. So barrier kind of disable the compiler optimizations that can change the order of execution? – cheems Jul 30 '23 at 18:40
  • 1
    Kind of. The only thing that the compiler doesn't reorder is the instruction for the volatile variable, but it can reorder anything that happens up to that point and after that point. The point when the variable changes is a memory barrier. I think this other answer explains the behaviour in a lot more detail that I'm giving it here: https://stackoverflow.com/questions/24469063/behavior-of-memory-barrier-in-java . And I should add that don't panic if you don't get it the first time, this is one of the most complicated aspects of the JVM. – Augusto Jul 30 '23 at 19:26
  • 2
    The correct terminology is the happens-before relation. So if variable A is volatile, when a thread sees a particular write of A, then there is a happens-before edge between the write and the read and as a consequence, it should see everything before that write as well. Be careful with the word barrier since that is an implementation detail and once people start to translate the JMM to barriers, a lot of problems tend to pop up, e.g. https://shipilev.net/blog/2016/close-encounters-of-jmm-kind/#myth-barriers-are-sane. But the gist of your story is correct. – pveentjer Aug 01 '23 at 02:45
  • 1
    That's an excellent explanation/guide - thanks for sharing it @pveentjer! – Augusto Aug 01 '23 at 09:00