27 June 2012

The ++ operator is not thread safe

Findbugs is great tools for analyzing java code. It can find potential bugs. One of the warnings is the VO: An increment to a volatile field isn't atomic (VO_VOLATILE_INCREMENT). It means that the ++ operator is not an atomic operation and thus not thread safe. To demonstrate this looks at this code:

public class PlusPlusOperatorThreadSaftey {
    @Test
    public void testThreadSaftey() throws InterruptedException {
        class IntegerHolder {
            private volatile int value = 0;
            private void increase() {
                value++;
            }
            private int getValue() {
                return value;
            }
        }
        
        final IntegerHolder integerHolder = new IntegerHolder();
        final int numberOfIncreasePerThread = 50;
        final int numberOfThreads = 100;
        
        ExecutorService threadPool = Executors.newFixedThreadPool(numberOfThreads);
        for (int i = 0; i < numberOfThreads; i++) {
            threadPool.submit(new Runnable() {
                public void run() {
                    for (int i = 0; i < numberOfIncreasePerThread; i++) {
                        integerHolder.increase();
                    }
                }
            });
        }
        
        threadPool.shutdown();
        threadPool.awaitTermination(10, TimeUnit.SECONDS);
        
        assertEquals(integerHolder.getValue(), numberOfIncreasePerThread *
            numberOfThreads);
    }
}
When running this code on my dual core CPU sometimes the test case passes, and sometimes I get:
java.lang.AssertionError: expected:<4998> but was:<5000>. Thus, the ++ operator is not atomic and updates can be lost.

To fix this you could add a synchronized block, but a better approach is to use an AtomicInteger like this:
class IntegerHolder {
        private AtomicInteger value = new AtomicInteger(0);
        private void increase() {
            value.incrementAndGet();
        }
        private int getValue() {
            return value.get();
        }
    }

No comments:

Post a Comment