2

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.

Dinesh Babu K G
  • 528
  • 6
  • 23
  • I guess you are expecting that [writing to a `volatile` flushes all values](https://stackoverflow.com/questions/17035487/what-is-the-scope-of-memory-flushed-or-published-to-various-threads-when-using-v), but [that is not what happens](https://stackoverflow.com/questions/7129015/what-happens-after-writing-to-a-volatile-variable). – Raedwald Mar 13 '19 at 10:20
  • Whenever I update data (volatile variable), I expect any thread reading it to see its latest value. I don't understand what do you mean by "flushes all values". – Dinesh Babu K G Mar 13 '19 at 10:36

2 Answers2

1

your volatile boolean data is NOT a reference, which I think you are trying to suggest here, but a copy. So the data in Reader will never be set to false. If you use Writer.data in Reader (and make it public), this should work.

Axel Podehl
  • 4,034
  • 29
  • 41
  • better :-) Does it work ? Also note that there is an AtomicBoolean in Java. since you are already using AtomicInteger, it would be more consitent to use AtomicBoolean instead of Boolean – Axel Podehl Mar 13 '19 at 10:05
0

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;
    }
}
Dinesh Babu K G
  • 528
  • 6
  • 23