Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Different kind of singleton pattern

Tags:

java

java-8

I'm studying basic software design pattern.

The basic implementation of singleton classes are written like this:

    public class MyObject{

       private volatile static MyObject obj;

       private MyObject(){/*Do some heavy stuff here*/}

       public static synchronized MyObject getInstance(){
         if(obj==null)
            obj=new MyObject();
         return obj;
       }    
    }

But as I have undestood calling synchronized methods can be heavy.

I while back I red a book the introduced this kind of implementation of Singleton class:

public class MyObjectHolder {

private volatile static Supplier<MyObject> myObjectSupplier = () -> createMyObj();
//myObjectSupplier is changed on the first 'get()' call

public static MyObject getMyObject(){
    return myObjectSupplier.get();
}

private static synchronized MyObject createMyObj(){
    class MyObjectFactory implements Supplier<MyObject> {
        private final MyObject clockTimer = new MyObject();
        public MyObject get() { return clockTimer; }
    }


    if(!MyObjectFactory.class.isInstance(myObjectSupplier)) {
        myObjectSupplier = new MyObjectFactory();
    }
    return myObjectSupplier.get();
}

public static class MyObject{

    private MyObject(){
        /*Do some heavy stuff here*/
    }

    public void someMethod(){
        /* ... */
    }
}

}


...   


    {
        /*In main MyObject instantiation*/
        MyObjectHolder.MyObject obj = MyObjectHolder.getMyObject();

    }

Now after the first call for 'createMyObj()' has been has been finished, there is no heavy burden of synchronized method calling and the is no if check neither.

Do you think there is something wrong with this kind of implementation?

ps. MyObject does not have to be an inner class of MyObjectHold but I thought it looked nice.

like image 718
Nabuska Avatar asked Dec 25 '22 09:12

Nabuska


2 Answers

[UPDATED] Another solution that is called Initialization on Demand Holder idiom :

public class SingletonObject {

    private static final AtomicInteger INSTANCE_COUNT = new AtomicInteger();
    private static final AtomicInteger INVOKE_COUNT = new AtomicInteger();

    private static final class LazyHolder {

        private static final SingletonObject INSTANCE = new SingletonObject();

    }

    private SingletonObject() {
        System.out.println("new SingletonObject");
        INSTANCE_COUNT.getAndIncrement();
    }

    public static SingletonObject getInstance() {
        INVOKE_COUNT.getAndIncrement();
        return LazyHolder.INSTANCE;
    }

    public static int getInstanceCount() {
        return INSTANCE_COUNT.get();
    }

    public static int getInvokeCount() {
        return INVOKE_COUNT.get();
    }

}

to prove that it's thread-safe :

public static void main(String[] args) throws Exception {
    int n = 1000;
    List<Callable<SingletonObject>> invokers = new ArrayList<>();
    for (int i = 0; i < n; i++) {
        invokers.add(SingletonObject::getInstance);
    }
    ExecutorService es = Executors.newFixedThreadPool(n);
    es.invokeAll(invokers);
    es.shutdown();
    System.out.println("Number of Instances = " + SingletonObject.getInstanceCount());
    System.out.println("Number of Invokes = " + SingletonObject.getInvokeCount());
}

Output :

new SingletonObject
Number of Instances = 1
Number of Invokes = 1000

EDIT (after @Holger's comment) :

the use of the Nested Holder Class is somewhat necessary to Lazily Initialize the SingletonObject.

public class SingletonObject {

    private static final SingletonObject INSTANCE = new SingletonObject();

    private SingletonObject() {
        System.out.println("new SingletonObject");
    }

    public static SingletonObject getInstance() {
        return INSTANCE;
    }

    public static void anotherStaticMethod() {
        System.out.println("I don't need the SingletonObject Instance...");
    }

}

So what happens if someone invokes the anotherStaticMethod()?

new SingletonObject
I don't need the SingletonObject Instance...

UPDATE :

The page at WIKIPEDIA says :

The implementation of the idiom relies on the initialization phase of execution within the Java Virtual Machine (JVM) as specified by the Java Language Specification (JLS). When the class SingletonObject is loaded by the JVM, the class goes through initialization. Since the class does not have any static variables to initialize, the initialization completes trivially. The static class definition LazyHolder within it is not initialized until the JVM determines that LazyHolder must be executed. The static class LazyHolder is only executed when the static method getInstance is invoked on the class SingletonObject, and the first time this happens the JVM will load and initialize the LazyHolder class. The initialization of the LazyHolder class results in static variable INSTANCE being initialized by executing the (private) constructor for the outer class SingletonObject. Since the class initialization phase is guaranteed by the JLS to be serial, i.e., non-concurrent, no further synchronization is required in the static getInstance method during loading and initialization. And since the initialization phase writes the static variable INSTANCE in a serial operation, all subsequent concurrent invocations of the getInstance will return the same correctly initialized INSTANCE without incurring any additional synchronization overhead. This gives a highly efficient thread-safe "singleton" cache, without synchronization overhead; benchmarking indicates it to be far faster than even uncontended synchronization. However, the idiom is singleton-specific and not extensible to pluralities of objects (e.g. a map-based cache).

Also keep an eye on this.

like image 79
FaNaJ Avatar answered Dec 26 '22 23:12

FaNaJ


The easiest way to implement the Singleton pattern in java is to simply make the class an enum instead:

public enum MyObject{

   Obj;

   MyObject(){/*Do some heavy stuff here*/}

}

Obj is guaranteed by the specification to only be created once on the first use of it.

like image 25
Jack Ammo Avatar answered Dec 26 '22 21:12

Jack Ammo