Search code examples
javamultithreadingthread-safetysynchronizedscjp

How can I make a public static unsynchronized getInstance() method return multiple instances of a private static reference variable to an object?


One of the SCJP practice exam questions I ran across supplied the code in the SafeDeposit class. The answer to the question claimed that if another class used multiple threads that it would be possible for the unsynchronized (non thread safe) getInstance() method to return multiple instances of SafeDeposit. I have tried, and tried and cannot get the toString() method to indicate that there is ever more than one SafeDeposit instance created. Am I missing something, or is this just one of those things that "could" happen but is really, really, really unlikely to happen?

class SafeDeposit {
    private static SafeDeposit sd;
    public static SafeDeposit getInstance() {
        if(sd == null) sd = new SafeDeposit();
        return sd;
    }
    private SafeDeposit() { }
}

public class PrivCon {
    public static void main(String[] args) {
        String checker;
        SafeThief wizard = new SafeThief();
        SafeThief wizard2 = new SafeThief();
        for(int i = 0; i < 10; i ++) {
            new Thread(wizard).start();
            new Thread(wizard2).start();
        }
    }
}

class SafeThief implements Runnable {
    public void run() {
        System.out.println(SafeDeposit.getInstance().toString());
    }
}

Solution

  • is this just one of those things that "could" happen but is really, really, really unlikely to happen?

    Try this code and see how unlikely it really is:

    class SafeDeposit {
      private static SafeDeposit sd;
      public static SafeDeposit getInstance() {
        if(sd == null) sd = new SafeDeposit();
        return sd;
      }
      private SafeDeposit() { }
    
      static void warmup() {
        for (int i = 0; i < 100_000; i++) getInstance();
        sd = null;
      }
    }
    
    public class PrivCon {
      public static void main(String[] args) {
        SafeDeposit.warmup();
        SafeThief wizard = new SafeThief();
        for(int i = 0; i < 10; i ++) new Thread(wizard).start();
      }
    }
    
    class SafeThief implements Runnable {
      public void run() {
        try { Thread.sleep(100); } catch (InterruptedException e) {  }
        System.out.println(SafeDeposit.getInstance().toString());
      }
    }
    

    This is my typical output:

    test.SafeDeposit@52e5376a
    test.SafeDeposit@34780af5
    test.SafeDeposit@351775bc
    test.SafeDeposit@2b1be57f
    test.SafeDeposit@6ae6235d
    test.SafeDeposit@6276e1db
    test.SafeDeposit@52e5376a
    test.SafeDeposit@302b2c81
    test.SafeDeposit@60f00e0f
    test.SafeDeposit@1732a4df
    

    Hardly any duplicates at all.

    If you want to know why, it's because I added warmup code, which caused the getInstance() method to be JIT-compiled into an aggressively optimized piece of code which leverages the liberties given by the Java Memory Model.

    I also added some sleep time to the beginning of the Runnable because as soon as one thread writes the value, those threads which start after that point will reliably observe the write. So it is better to first let all threads start, then let them call getInstance.