Code to simulate race condition in Java thread
The simplest kind of race condition is where two threads are updating some shared data using this pattern
read a value
think for a bit, giving another thread a chance to get in
increment the value and write it back
So now if you have two threads running, each incrementing a counter whose initial value is 43, we expect this
A reads value 43
A thinks
A increments and writes 44
B reads value 44
B thinks
B increments and writes 45
but this could happen, because of the "think window"
A reads value 43
A thinks
B reads value (it's still) 43
B thinks
B increments 43 to 44 and writes
A increments 43 to 44 and write
// the value is now 44, and we expected it to be 45
The key idea for a race is that you get unexpectedly bad effects, for example in an inventory application, two threads each decrement the amount of stock, and just like in the example above we "lose" one of the decrements.
Now your code has two issues:
1). no shared values so we have no chance to see any such contention
2). You're incrementing an integer in a single line of code, so there's very little chance of two threads clashing. In simulating a race it's better to separate the read and write as I show above, and then create a "window of opportunity" by sleeping to simulate thinking time. In multi-processor environment where threads may truly be running in parallel even a single line of code could conceivably get a race because the JVM will internally be doing reads and writes and may even keep a cache of the values.
In order to have a race between two threads, there must be shared state between those two threads and interaction (reading and writing) to that state must occur outside of a mutualy exclusive block (aka syncronized). Reading, incrementing and then writing back to a volatile field outside of a synchronized block is a great example of this.
For example, consider this situation documented on this blog.
Both thread A and B could read the counter before any modification occurs. They then both increment, and they then both write. The end result will then be 18, and not 19. For it to have been 19, we would have needed thread B to read the counter AFTER thread A had written to the counter. Which, can happen sometimes. That is why it is called a race.
To reliably achieve this kind of race, change your test code above to create the counter outside of the threads and then pass it in to them via their constructors.
The second problem that you have is that the window for the operations to overlap is very fine, and given that starting a thread has, in comparison a lot of over head then the chances of these three threads over lapping at just the right time is very low. Thus to increase their odds, you should repeat the runs in a tight loop.
The following code demonstrates the two concepts above. The changes made have been:
- renamed classes to make their use a little clearer
- shared the state of MyCounter between the two threads
- tight loop within each thread, calling increment 1,000,000 times
- the main thread now blocks using join() waiting for the two threads to complete, this replaces the Thread.sleep that you had earlier
- the counter value c in MyCounter is now volatile; this tells the JVM to always go out to shared memory for the value and not to optimise by keeping it within a register between encounters. to make the race much worse, take volatile off and see what happens :)
- the main loop then finishes by printing out the value of the counter, which should be 2,000,000. but it will not be due to the race that is going on over the volatile counter.
.
public class CounterTest {
public static void main(String[] args) throws InterruptedException {
MyCounter counter = new MyCounter();
Thread thread1 = new Thread(new CounterIncRunnable(counter));
thread1.setName("add thread");
thread1.start();
Thread thread2 = new Thread(new CounterIncRunnable(counter));
thread2.setName("add thread2");
thread2.start();
thread1.join();
thread2.join();
System.out.println(counter.value());
}
}
class CounterIncRunnable implements Runnable {
private MyCounter counter;
public CounterIncRunnable(MyCounter counter) {
this.counter = counter;
}
public void run() {
for ( int i=0; i<1000000; i++ ) {
counter.increment();
}
}
}
class MyCounter {
private volatile int c = 0;
public void increment() {
c++;
}
public void decrement() {
c--;
}
public int value() {
return c;
}
}
Finally, just for fun; add synchronized to the increment method of MyCounter and then rerun. The race condition will disappear, and now the program will correctly print 2000000. This is because every call to increment will now only allow one thread in to the shared method at a time. Thus serializing each access to the shared variable c, and putting an end to the race.