I got this exercise at uni:
Write a program that declares a shared integer counter and then creates two threads, one of which attempts to increment the counter 1000 times and the other attempts to decrement the counter 1000 times. When each thread has finished looping, it should print out the final value of the counter. (Hint: You will need to define a Counter class. Why?) What do you think the output should be? Did the program work as you expected? Try running the program repeatedly to see if you always get the same result.
I ran the program expecting the final result to be zero, but it actually outputs a different number between 0 and 1000 each time. Can anyone tell me why? Thanks.
public class Counter
{
private int val;
public Counter()
{
val = 0;
}
public void increment()
{
val = val + 1;
}
public void decrement()
{
val = val - 1;
}
public int getVal()
{
return val;
}
}
public class IncThread extends Thread
{
private static final int MAX = 1000;
private Counter myCounter;
public IncThread(Counter c)
{
myCounter = c;
}
public void run()
{
for (int i = 0; i < MAX; i++)
{
myCounter.increment();
}
}
}
public class DecThread extends Thread
{
private static final int MAX = 1000;
private Counter myCounter;
public DecThread(Counter c)
{
myCounter = c;
}
public void run()
{
for (int i = 0; i < MAX; i++)
{
myCounter.decrement();
}
}
}
public class Main
{
public static void main(String[] args)
{
Counter c = new Counter();
Thread inc = new IncThread(c);
Thread dec = new DecThread(c);
inc.start();
dec.start();
System.out.println(c.getVal());
}
}
At the lowest level, your code probably boils down to something like:
Because of that and the fact that threads can be stopped and started at any point of their execution, you have the possibility of this:
You can see that, although both threads have finished exactly one of the thousand cycles and you expect the count to be zero, it is actually one.
When sharing variables among threads of execution, you need to ensure that reads, writes and updates are atomic (there are exceptions but they’re rare).
To do this, you should look into the various operations provided in your environment for just this purpose (such as synchronisation features in the language, mutexes (mutual exclusion semaphores) or atomic variables.