javamultithreadingconcurrencyatomicreference

Data Races in an AtomicIntegerArray


In the code below: I am updating num[1]=0 of an AtomicIntegerArray num 1000 times each in 2 threads.

At the end of the 2 threads in main thread ;shouldn't the value of num[1] be 2000 as there shouldn't be data races in an AtomicIntegerArray .

However I get random values < 2000. Could someone tell me why?

Code:

import java.util.concurrent.atomic.AtomicIntegerArray;

public class AtomicIntegerArr {

    private static AtomicIntegerArray num= new AtomicIntegerArray(2);

    public static void main(String[] args) throws InterruptedException {
        Thread t1 = new Thread(new MyRun1());
        Thread t2 = new Thread(new MyRun2());

        num.set(0, 10);
        num.set(1, 0);

        System.out.println("In Main num before:"+num.get(1));

        t1.start();
        t2.start();

        t1.join();
        t2.join();

        System.out.println("In Main num after:"+num.get(1));
    }

    static class MyRun1 implements Runnable {
        public void run() {
            for (int i = 0; i < 1000; i++) {
                num.set(1,num.get(1)+1);
            }

        }
    }

    static class MyRun2 implements Runnable {
        public void run() {
            for (int i = 0; i < 1000; i++) {
                num.set(1,num.get(1)+1);
            }

        }

    }

}

Edit: Adding num.compareAndSet(1, num.get(1), num.get(1)+1); instead of num.set(1,num.get(1)+1); doesnt work either.


Solution

  • I get random values < 2000. Could someone tell me why?

    This is called the lost-update problem.

    Because, in the following code:

    num.set(1, num.get(1) + 1);
    

    Although each individual operation involved is atomic, the combined operation is not. The single operations from the two threads can interleave, causing updates from one thread to be overwritten with stale value by another thread.

    You can use compareAndSet to solve this problem, but you have to check whether the operation is successful, and do it again when it fails.

    int v;
    do {
        v = num.get(1);
    } while (!num.compareAndSet(1, v, v+1));
    

    There's also a method for exactly this purpose:

    num.accumulateAndGet(1, 1, (x, d)->x+d);
    

    accumulateAndGet(int i, int x, IntBinaryOperator accumulatorFunction)

    Atomically updates the element at index i with the results of applying the given function to the current and given values, returning the updated value. The function should be side-effect-free, since it may be re-applied when attempted updates fail due to contention among threads. The function is applied with the current value at index i as its first argument, and the given update as the second argument.