Java race condition between threads and solution

package com.bawi.threads;

public class MyRaceConditionTest {

    static class Counter {
        private long value = 0;

        public void add(long v) {
            value = value + v;
        }

        public long getValue() {
            return value;
        }
    }

    public static void main(String[] args) throws InterruptedException {
        while (true) {
            Counter counter = new Counter();

            Thread t1 = new Thread(() -> counter.add(1));
            Thread t2 = new Thread(() -> counter.add(2));
            t1.start();
            t2.start();

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

            long value = counter.getValue();
            System.out.println("value=" + value);

            if (value != 3L) {
                throw new RuntimeException("value=" + value);
            }
        }
    }
}

/*
value=3
...
value=1
Exception in thread "main" java.lang.RuntimeException: value=1
	at com.bawi.threads.MyRaceConditionTest.main(MyRaceConditionTest.java:33)
*/

The add method execution consists of 3 steps: read value from memory to register, add value to the register, write new register value to memory. However, operating system can switch between threads at any time. So it possible the following order happened:
1) t1 reads value 0 from memory to t1 register,
2) t2 reads value 0 from memory to t2 register,
3) t2 increases register value from 0 to 2,
4) t2 writes register value ‘2’ to memory,
5) t1 increases register value from 0 to 1,
6) t1 writes register value ‘1’ to memory.
The race condition is when the result depends on the order how threads execute critical section (one or more threads writes to the resource). Race condition does not occurs if there are only reads and no writes.

Solution:
1) apply synchronized at method level (public synchronized void add…)
2) apply synchronized at code level (synchronized (this) { …. } )
3) apply ReentantLock:

    static class ReentrantLockCounter {
        private Lock lock = new ReentrantLock();
        private long value = 0;

        public void add(long v) {
            lock.lock();
            value = value + v;
            lock.unlock();
        }
        public long getValue() {
            return value;
        }
    }

4) apply AtomicLong:

    static class AtomicCounter {
        private AtomicLong value = new AtomicLong();
        public void add(long v) {
            value.getAndAdd(v);
        }
        public long getValue() {
            return value.get();
        }
    }
Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s