0

In Java Double checked locking (DCL) attempts to improve performance of lazy instantiation but without memory barriers is exposed to dangerous race conditions between the setting of a variable's address and the publishing of its value to main memory.

So my question is:

Can someone provide an example Java program/snippet, that reliably shows an incorrect value read as a result of DCL without volatile on the variable?

Here is my (failed ) attempt at it.

class Foo {
    String field = null; // not volatile
    String getField() {
        if( field == null )
        {
            synchronized (this) {
                if ( field == null )
                    field = new String("abc");
            }
        }
        return field;
    }
}

class MyRunnable implements Runnable {

    @Override
    public void run() {
        if ( !Example.foo.getField().equals("abc") ) {
            System.out.println("Got an uninitialized FIELD !!!");
        }
    }
    
}
class Example {
    static Foo foo;
    public static void main(String[] args) throws InterruptedException {
        for( int i = 0; i < 1000000; ++i ) {
         foo = new Foo();
         Thread t1 = new Thread(new MyRunnable());
         Thread t2 = new Thread(new MyRunnable());
         t1.start();
         t2.start();
         t1.join();
         t2.join();
        }
    }
}

Obviously not being able to prove it doesn't mean that DCL is safe, but it would still be nice to have an obvious example that shows that it is dangerous.

Gonen I
  • 5,576
  • 1
  • 29
  • 60
  • I would give it a go using https://openjdk.java.net/projects/code-tools/jcstress/. The problem isn't a race condition btw, but a data race. Also forget about things being published to main memory. Caches are always coherent. – pveentjer Jun 18 '21 at 03:06
  • Also keep in mind that memory barriers are a hardware level concern. The compiler can also mess things up, so you need a compiler barrier as well. It would be good enough to put a [StoreStore] between the construction of the object to the assignment of the object and a [LoadLoad] between the load of the object and the usage of its fields (add before return). This would create the required happens before edge. Look in VarHandle for these fences. – pveentjer Jun 18 '21 at 03:13

0 Answers0