Search code examples
javamultithreadingconcurrencyatomicvolatile

Volatile variable's updated value is not visible to other threads


The following snippet of code uses multiple threads to count to 100 million using an AtomicInteger. I have 10 Writer threads to simulate write contention and a single Reader thread to simulate read contention. The Writer and Reader also share a volatile boolean variable as a poison pill.

However, only the Reader thread is able to exit while the Writers are not. Despite declaring the variable as volatile, why aren't the Reader threads able to see the updated value?

public class AtomicRunnerV2 {

    private static final int THREADS = 10;
    private static final Integer MAX_COUNT = 100_000_000;

    public static void main(String[] args) throws Exception {
        AtomicInteger atomicInteger = new AtomicInteger(1);
        Boolean data = new Boolean(false);
        ExecutorService executorService = Executors.newFixedThreadPool(THREADS + 1);
        for (int i = 0; i < THREADS; i++) {
            executorService.submit(new Writer(atomicInteger, data));
        }
        executorService.submit(new Reader(atomicInteger, data));
        executorService.shutdown();
    }

    static class Writer implements Runnable {

        private AtomicInteger integer;
        private volatile Boolean data;

        Writer(final AtomicInteger integer, Boolean data) {
            this.integer = integer;
            this.data = data;
        }

        @Override public void run() {
            while (!data) {
                integer.incrementAndGet();
            }
            System.out.println("count " + integer.get() + " from WRITER!");
        }
    }

    static class Reader implements Runnable {

        private AtomicInteger integer;
        private volatile Boolean data;

        Reader(final AtomicInteger integer, Boolean data) {
            this.integer = integer;
            this.data = data;
        }

        @Override public void run() {
            while (!data) {
                if (integer.get() >= MAX_COUNT) {
                    data = true;
                }
            }
            System.out.println("count " + integer.get() + " from READER!");
        }
    }
}

P.S: If I wrap the boolean in an object, it works.


Solution

  • Boolean is immutable.

    data = true;
    

    The above line in Writer essentially creates a new Boolean instance thereby causing the Readers to never see the new value as nothing was "updated" in their Boolean reference.

    Tested this by replacing the all Boolean occurrences in my code with a custom class like below and the Reader threads are able to see the updated value.

    static class MyBoolean {
    
        private boolean value;
    
        MyBoolean(final boolean value) {
            this.value = value;
        }
    
        boolean isValue() {
            return value;
        }
    
        void setValue(final boolean value) {
            this.value = value;
        }
    }