Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why is the output of this race condition somewhat consistent?

When I run the following code, the final output is always positive, and if I switch the order of the "x++" and the "x--" then the final output is always negative. Is there some semblance of order to which parts of this race condition get skipped? Any help understanding is greatly appreciated!

public class DataRace {

    private static class MyThreadCode implements Runnable {

        private static int x = 0;   // NOTE THAT X IS STATIC!!!

        @Override
        public void run() {
            for (int i = 0; i < 10000000; i++) {
                x++;
                x--;
            }
            System.out.println(x + "  " + Thread.currentThread().getName());
        }
    }
    
    public static void main(String[] args) {
        for (int i = 0; i < 100; i++) {
            Thread t = new Thread(new MyThreadCode());
            t.start();
        }
    }
}
like image 230
Matthew Foulk Avatar asked Apr 14 '21 15:04

Matthew Foulk


People also ask

What is a race condition and why is it critical to prevent it?

A race condition is an undesirable situation that occurs when a device or system attempts to perform two or more operations at the same time, but because of the nature of the device or system, the operations must be done in the proper sequence to be done correctly.

Why does race condition occur?

When race conditions occur. A race condition occurs when two threads access a shared variable at the same time. The first thread reads the variable, and the second thread reads the same value from the variable.

What is race condition and how it can be eliminated?

It can be eliminated by using no more than two levels of gating. An essential race condition occurs when an input has two transitions in less than the total feedback propagation time. Sometimes they are cured using inductive delay line elements to effectively increase the time duration of an input signal.

What is the difference between race condition and data races are they the same?

A race condition occurs when the timing or order of events affects the correctness of a piece of code. A data race occurs when one thread accesses a mutable object while another thread is writing to it.


1 Answers

We have two problems here:

  • Because x++ and x-- are not atomic (see Why is i++ not atomic?), you get a race condition. Thread 1 will load value of x (0). The CPU could then switch to Thread 2, which also loads the current x (0). Now both increment the value locally and later, they will set x. This means we lose an increment.
  • Because x is not marked volatile nor are we using the synchronized keyword, you have no guarantee that a thread really sees the actual value of x. It could be, that the value has already been updated by another thread, but because of the Java visibility guarantees, you have no guarantee what the other thread sees (the update value or some outdated "cached" value). It may also be that the other Thread did not yet write the update x value back to the memory (only to L1/L2 Cache).

I played a bit with the code and if I reduce the for loop to 1000 iterations, I get both negative and positive values for the same code. This could be a hint that the JVM optimizes the code if we have many iterations. If we only have 1000 iterations, which is not that much i guess, the JVM may decide to run the code as it is written.

I suspect both problems have some influence on the result. For example, if you load x, the processor will probably load that value into the L1 cache, and if you then execute the second operation, it may load that value directly from the L1 cache instead of the main memory. This could mean, that the 2nd operation causes no/less "lost updates".

But to really find out why that happens, I think you would to dig into the Java specification or even how the CPU handles such cases.

like image 143
skeeks Avatar answered Sep 23 '22 06:09

skeeks