Search code examples
javamultithreadingrace-condition

Potential Java Race Conditions


Trying to understand the following claim:

A data race occurs when 2 or more threads try to access the same non-final variable without synchronization. Not using synchronization may lead to making changes which are not visible to other threads, so reading the stale data is possible, which in turn may have consequences such as infinite loops, corrupted data structures, or inaccurate computations. This code might result in an infinite loop, because the reader thread may never observe the changes made by the writer threads:

class Waiter implements Runnable {
  private boolean shouldFinish;

  void finish() { shouldFinish = true; }

  public void run() {
    long iteration = 0;
    while (!shouldFinish) {
      iteration++;
    }
    System.out.println("Finished after: " + iteration);
  }
}

class DataRace {

  public static void main(String[] args) throws InterruptedException {
    Waiter waiter = new Waiter();
    Thread waiterThread = new Thread(waiter);
    waiterThread.start();

    waiter.finish();
    waiterThread.join();
  }
}

My thinking is that,

  • In Java Reader/Writer primitive data like boolean is atom action.
  • Even if the reader thread failed to observe the changes made by the writer threads in one loop, it should be able to see the change in the next loop.

No the case? Why? Because the reader thread may never yield to the writer thread? (If so I don't think it shall be called Race Conditions, right?)


Solution

  • No the case?

    No.

    Each thread is free to create a local cached copy of any field (and, generally, they do). Until there is a reason to update these caches, the JVM probably wont, for days if it wants to (the spec does not impose any requirement on a JVM to do so).

    To force an update, you use volatile, synchronized, or any other Happens Before relationship as listed in the Java Memory Model section of the java memory spec. Your code has zero such HB relationships, therefore, the JVM doesn't ever have to update.

    In other words, one thread thinks shouldFinish is true, another thinks it is false, and they continue to think that for hours, days, or forever, or not - the JVM spec simply doesn't specify either way.