3

I'm trying to learn the concept of thread interference and have encountered the following example in Java Tutorial Oracle:

class Counter {
    private int c = 0;

    public void increment() {
        c++;
    }

    public void decrement() {
        c--;
    }

    public int value() {
        return c;
    }

}

The Oracle tutorial mentioned that if there are two threads trying access variable c, it can result in thread interference where the change made by one thread is not seen by the other.

However, it did not provide any code to actually explain the concept. Could someone please provide a code example based the Counter class to demonstrate how thread interference actually happens?

Ravindra babu
  • 37,698
  • 11
  • 250
  • 211
Thor
  • 9,638
  • 15
  • 62
  • 137

3 Answers3

3

Rather than the code, I prefer explaining what will happen. Suppose 2 threads, A and B are accessing the same counter object, A calls increment and B calls decrement. Either of these operations consist of at least 3 steps.

  1. Read C from memory
  2. Increment or decrement C
  3. Write back C to the memory.

When A and B try to increment and decrement at the same time, One thread may read C from memory(step 1), while the other thread is at step 2. in such cases if the initial value of c was 5, first Thread A read and increment it to 6. Then Thread B read and decrement it to 4. Remember, B is doing these changes before A finish writing c back to the memory. Since the steps are overlapped, the changes made by one thread will not be visible to the other resulting in the final value of c being either 6 or 4. But actually what we expected was 5.

This is an example of two threads interfering each other. To avoid this we use thread synchronization.

Imesha Sudasingha
  • 3,462
  • 1
  • 23
  • 34
1

Just run the code a lot of times and one of these lucky times you will be able to see the Thread Interference in action.

It is rare to observe in this case because its a small program and not much is happening.If you make multiple increment and decrement Threads, Thread Interference will be easier to observe.

class Counter {
private int c = 0;

public void increment() {c++;}

public void decrement() {c--;}

public int value() {
    return c;
}

public static void main(String[] args) {

    Counter x = new Counter();
    Runnable r1 = new Runnable() {
        @Override
        public void run() {
            x.increment();
        }
    };

    Runnable r2 = new Runnable() {
        @Override
        public void run() {
            x.decrement();
        }
    };

    Thread t1 = new Thread(r1);
    Thread t2 = new Thread(r2);     
    t1.start();
    t2.start();
    System.out.println(x.c);

}

}

Edit : I decided to add the multiple thread case , I couldn't resist

Edit 2 : This is a second edit.The multiple thread case since that was creating issues beyond the scope of this question I decided to remove it.previously I was making an array of threads and running them.Instead it will be better to show a thread that does a lot of increment and another that does a lot of decrement.

I have used Thread.Sleep() Causing The main thread to sleep which will make sure to print c after both threads are done operating on it.

class Counter {
private int c = 0;

public void increment() {
    for (int i = 0; i < 10000; i++) {
        c++;
    }

    }

public void decrement() {
    for (int i = 0; i < 5000; i++) {
        c--;
    }
    }

public int value() {
return c;
}

public static void main(String[] args) {

Counter x = new Counter();
Runnable r1 = new Runnable() {
    @Override
    public void run() {
        x.increment();
    }
};

Runnable r2 = new Runnable() {
    @Override
    public void run() {
        x.decrement();
    }
};

Thread t1 = new Thread(r1);
Thread t2 = new Thread(r2);     
t1.start();
t2.start();
try {
    Thread.sleep(2000);
} catch (InterruptedException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
}
if(!(t1.isAlive() && t2.isAlive()))
System.out.println(x.c);//expected answer 5000
}
}

Note:Synchronized Increment/Decrement methods give the correct answer.Try yourself.

Ishan Dave
  • 121
  • 5
1

Create multiple threads and call increment(), decrement() and value() from those Threads.

Sample code will be like this:

class Counter {
    private int c = 0;

    public void increment() {
        c++;
    }

    public void decrement() {
        c--;
    }

    public int value() {
        return c;
    }

    public static void main(String args[]){
        Counter c = new Counter();
        for ( int i=0; i<3; i++){
            Thread t = new Thread(new MyRunnable(c));
            t.start();
        }
    }
}
class MyRunnable implements Runnable{
    Counter counter;
    public MyRunnable(Counter c){
        counter = c;
    }
    public void run(){
        counter.increment();
        System.out.println("Counter value after increment:"+counter.value()+" from thread:"+  Thread.currentThread().getName());
        counter.decrement();
        System.out.println("Counter value after decrement:"+counter.value()+" from thread:"+  Thread.currentThread().getName());
    }
}

output: ( This output will vary for each run )

Counter value after increment:1 from thread:Thread-0
Counter value after decrement:2 from thread:Thread-0
Counter value after increment:2 from thread:Thread-2
Counter value after decrement:1 from thread:Thread-2
Counter value after increment:3 from thread:Thread-1
Counter value after decrement:0 from thread:Thread-1

Now from the output, you can understand thread interference. To fix this issue, you can use AtomicInteger.

Have a look at below post for more details:

Why is synchronized not working properly?

Community
  • 1
  • 1
Ravindra babu
  • 37,698
  • 11
  • 250
  • 211