1

Here's my thread:

public class MyRunnable implements Runnable
{
  public static int num = 0;

  private void add()
  {
    num = num + 1;
  }

  @Override
  public void run()
  {
    for (int i=0;i<10000;i++)
    {
      add();
      System.out.println(num);
    }
  }
}

And here's my main:

public class MultiThread
{
  public static void main(String[] argv)
  {
    Thread mt1 = new Thread(new MyRunnable(), "A");
    Thread mt2 = new Thread(new MyRunnable(), "B");

    mt1.start();
    mt2.start();
  }

}

I'm expecting to see race conditions there and therefore the output should be less than 20000. However, the actual output I got was :

19975
19976
19977
19978
19979
19980
19981
19982
19983
19984
19985
19986
19987
19988
19989
19990
19991
19992
19993
19994
19995
19996
19997
19998
19999
20000

Process finished with exit code 0

Can anyone explain to me why in this java program the add operation seems to be atomic even when I didn't do any locking or synchronizing?

OneZero
  • 11,556
  • 15
  • 55
  • 92
  • 5
    Absence of an error in your result does not imply the absence of a (possible) race condition. – Greg Hewgill Sep 01 '14 at 00:19
  • I ran this piece of code many many times and never got a race condition, but I think I'm not that lucky not to get a race condition in more than hundreds of thousands of iterations. – OneZero Sep 01 '14 at 00:21
  • Try to increase number of iterations (lets say to `1000000`) and move printing statement outside of loop (place it after it to increase chances of race). – Pshemo Sep 01 '14 at 00:23
  • There is a reason that `AtomicInt` exists! –  Sep 01 '14 at 00:50

3 Answers3

3

You just haven't tried enough times or haven't looked at your results closely enough. This piece of code

private void add()
{
    num = num + 1;
}

is in no way safe. You're setting yourself up for lost updates. Both threads will read the same value of num and each will update it, so one increment will get lost.

Sotirios Delimanolis
  • 274,122
  • 60
  • 696
  • 724
  • 3
    Chances are that each thread spends so much *more* time doing I/O, that they don't happen to step on each other during the relatively miniscule amount of time it takes to do a single load-modify-store. – Greg Hewgill Sep 01 '14 at 00:23
  • @GregHewgill I see. Yeah the race condition appears after I remove the print statement. – OneZero Sep 01 '14 at 00:29
  • 2
    @OneZero Take a look at [this](http://stackoverflow.com/questions/25425130/loop-doesnt-see-changed-value-without-a-print-statement) as well for the effects of `System.out.println()`. – Sotirios Delimanolis Sep 01 '14 at 00:31
1

It's quite possible that the first thread finishes its (quite short) loop before the second thread starts its own loop, and therefore they appear to not interfere with each other.

You would see the expected behavior if you try longer loops or add delays within the loop.

Eran
  • 387,369
  • 54
  • 702
  • 768
0

Just to be more explicit with the notion of race condition in the case you presented; the problem, of course, is the area

num = num + 1;

To clarify why this is very unsafe and will result in a race condition, you need to look into the assembly code that defines it. Now, in Java you might think that this is only one line of code that being executed but that is far from the truth. Let me explain...

consider the following assembly line that

LOAD    @i, r0    ;load the value of 'i' into a register from memory
ADD     r0, 1     ;increment the value in the register
STORE   r0, @i    ;write the updated value back to memory

or in simple terms:

Fetch i into a register
Increment the register
Write it back to i

The race condition occurs when thread A fetch i into the register, increment it and then BEFORE writing back - thread B comes in and does the same - fetch & increment (oh no). It can be in any place in the 3 lines of code

So this code is considered unsafe and may result in a race condition. The fact that it doesn't happen in the 1000th time doesn't mean it won't happen.

Community
  • 1
  • 1
adhg
  • 10,437
  • 12
  • 58
  • 94