1

I know, that in theory, to implement a correct singleton, in addition to double checked locking and synchronized we should make an instance field volatile.

But in real life I cannot get an example, that would expose the problem. Maybe there is a JVM flag that would disable some optimisation, or allow runtime to do such code permutation?

Here is the code, that, as I understand, should print to console from time to time, but it doesn't:

class Data {
    int i;

    Data() {
        i = Math.abs(new Random().nextInt()) + 1; // Just not 0
    }
}

class Keeper {
    private Data data;

    Data getData() {
        if (data == null)
            synchronized (this) {
                if (data == null)
                    data = new Data();
            }
        return data;
    }
}

@Test
void foo() throws InterruptedException {
    Keeper[] sharedInstance = new Keeper[]{new Keeper()};
    Thread t1 = new Thread(() -> {
        while (true)
            sharedInstance[0] = new Keeper();
    });
    t1.start();
    final Thread t2 = new Thread(() -> {
        while (true)
            if (sharedInstance[0].getData().i == 0)
                System.out.println("GOT IT!!"); // This actually does not happen
    });
    t2.start();
    t1.join();
}

Could someone provide a code, that clearly demonstrates described theoretical lack of volatile problem?

Adamovskiy
  • 1,376
  • 1
  • 12
  • 42

1 Answers1

1

Very good article about it https://shipilev.net/blog/2014/safe-public-construction/

You can find examples in the end.

And be aware about

x86 is Total Store Order hardware, meaning the stores are visible for all processors in some total order. That is, if compiler actually presented the program stores in the same order to hardware, we may be reasonably sure the initializing stores of the instance fields would be visible before seeing the reference to the object itself. Even if your hardware is total-store-ordered, you can not be sure the compiler would not reorder within the allowed memory model spec. If you turn off -XX:+StressGCM -XX:+StressLCM in this experiment, all cases would appear correct since the compiler did not reorder much.

belbix
  • 168
  • 2
  • 12