I am new to Java multithreading. I am learning the concept of race condition.
Based on the Oracle document
http://docs.oracle.com/javase/tutorial/essential/
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:
.
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.