Java: Singleton

Singleton

Singleton is a creational design pattern, which ensures that only one object of its kind exists and provides single point of access to it for any other code.

Singleton has almost the same pros and cons as global variables. Although they are super-handy, they break modularity of your code.

You can just use a class, which depends on Singleton, in some other context. You'll have to carry the Singleton class as well. Most of the time, this limitation comes up during the creation of unit tests.

More about Singleton

Application of the pattern in Java

Complexity: Low

Popularity: Average

Usage examples: A lot of developers consider Singleton an antipattern. That's why its usage is on the decline in Java code.

Despite this, there are quite a lot of Singleton examples in Java core libraries:

Identification: Singleton can be recognized by a static creational method, which returns same cached object.

Example: Naïve Singleton (single-threaded)

It's pretty easy to implement a sloppy Singleton. You just need to hide constructor and implement a static creational method.

Singleton.java: Singleton

package refactoring_guru.singleton.example.non_thread_safe;

public final class Singleton {
    private static Singleton instance;
    public String value;

    private Singleton(String value) {
        // Following code emulates slow initialization.
        try {
            Thread.currentThread().sleep(1000);
        } catch (InterruptedException ex) {
            ex.printStackTrace();
        }
        this.value = value;
    }

    public static Singleton getInstance(String value) {
        if (instance == null) {
            instance = new Singleton(value);
        }
        return instance;
    }
}

DemoSingleThread.java: Client code

package refactoring_guru.singleton.example.non_thread_safe;

public class DemoSingleThread {
    public static void main(String[] args) {
        System.out.println("If you see the same value, then singleton was reused (yay!)" + "\n" +
                "If you see different values, then 2 singletons were created (booo!!)" + "\n\n" +
                "RESULT:" + "\n");
        Singleton singleton = Singleton.getInstance("FOO");
        Singleton anotherSingleton = Singleton.getInstance("BAR");
        System.out.println(singleton.value);
        System.out.println(anotherSingleton.value);
    }
}

OutputDemoSingleThread.txt: Execution results

If you see the same value, then singleton was reused (yay!)
If you see different values, then 2 singletons were created (booo!!)

RESULT:

FOO
FOO

Variation: Naïve Singleton (multithreaded)

Same class behaves incorrectly in a multithreaded environment. Multiple threads can call creational method simultaneously and get several instances of Singleton class.

Singleton.java: Singleton

package refactoring_guru.singleton.example.non_thread_safe;

public final class Singleton {
    private static Singleton instance;
    public String value;

    private Singleton(String value) {
        // Following code emulates slow initialization.
        try {
            Thread.currentThread().sleep(1000);
        } catch (InterruptedException ex) {
            ex.printStackTrace();
        }
        this.value = value;
    }

    public static Singleton getInstance(String value) {
        if (instance == null) {
            instance = new Singleton(value);
        }
        return instance;
    }
}

DemoMultiThread.java: Client code

package refactoring_guru.singleton.example.non_thread_safe;

public class DemoMultiThread {
    public static void main(String[] args) throws Exception {
        System.out.println("If you see the same value, then singleton was reused (yay!)" + "\n" +
                "If you see different values, then 2 singletons were created (booo!!)" + "\n\n" +
                "RESULT:" + "\n");
        Thread threadFoo = new Thread(new ThreadFoo());
        Thread threadBar = new Thread(new ThreadBar());
        threadFoo.start();
        threadBar.start();
    }

    static class ThreadFoo implements Runnable {
        @Override
        public void run() {
            Singleton singleton = Singleton.getInstance("FOO");
            System.out.println(singleton.value);
        }
    }

    static class ThreadBar implements Runnable {
        @Override
        public void run() {
            Singleton singleton = Singleton.getInstance("BAR");
            System.out.println(singleton.value);
        }
    }
}

OutputDemoMultiThread.txt: Execution results

If you see the same value, then singleton was reused (yay!)
If you see different values, then 2 singletons were created (booo!!)

RESULT:

FOO
BAR

Variation: Correct Singleton (thread-safe)

To fix the problem, you have to synchronize threads during first creation of Singleton object.

Singleton.java: Singleton

package refactoring_guru.singleton.example.thread_safe;

public final class Singleton {
    private static volatile Singleton instance;
    public static String value;

    private Singleton(String value) {
        this.value = value;
    }

    public static Singleton getInstance(String value) {
        if (instance == null) {
            synchronized (Singleton.class) {
                if (instance == null) {
                    instance = new Singleton(value);
                }
            }
        }
        return instance;
    }
}

DemoMultiThread.java: Client code

package refactoring_guru.singleton.example.thread_safe;

public class DemoMultiThread {
    public static void main(String[] args) {
        System.out.println("If you see the same value, then singleton was reused (yay!)" + "\n" +
                "If you see different values, then 2 singletons were created (booo!!)" + "\n\n" +
                "RESULT:" + "\n");
        Thread threadFoo = new Thread(new ThreadFoo());
        Thread threadBar = new Thread(new ThreadBar());
        threadFoo.start();
        threadBar.start();
    }

    static class ThreadFoo implements Runnable {
        @Override
        public void run() {
            Singleton singleton = Singleton.getInstance("FOO");
            System.out.println(singleton.value);
        }
    }

    static class ThreadBar implements Runnable {
        @Override
        public void run() {
            Singleton singleton = Singleton.getInstance("BAR");
            System.out.println(singleton.value);
        }
    }
}

OutputDemoMultiThread.txt: Execution results

If you see the same value, then singleton was reused (yay!)
If you see different values, then 2 singletons were created (booo!!)

RESULT:

BAR
BAR