Java Design Patterns--Singleton Pattern

1 Singleton singleton pattern

Purpose: Ensure object uniqueness;
Implementation: This pattern involves a class that is responsible for creating an object while ensuring that only one object is created. This class provides a way to access it's unique object, which can be accessed directly without instantiating an object of the class.

2 main implementation

2.1 Hungry Man Mode

Hungry mode does not lock, and the execution efficiency will be improved. It avoids the synchronization problem of multi-threading based on the classloder mechanism. In the singleton mode, most of them call static methods to obtain instances.

2.1.1 Hungry Man Mode

public class Singleton {
    // 饿汉模式 创建 Singleton 的一个对象
    private static Singleton singleton = new Singleton();
    // 让构造函数为 private,这样该类就不会被实例化
    private Singleton() {
    }
    // 获取唯一可用的对象
    public static Singleton getInstance() {
        return singleton;
    }
    public void showMessage() {
        System.out.println("Hello World!");
    }

}

2.2 Lazy Mode

The lazy man mode has multiple implementations because it is designed to be thread-safe.

2.2.1 Lazy mode lock-free

This method is the most basic implementation, and the biggest problem with this implementation is that it does not support multithreading.
Because there is no lock synchronization, it is not strictly a singleton mode.
This method of lazy loading is obvious, does not require thread safety, and does not work well in multi-threading.

public class SingletonLazy {
    private static SingletonLazy singletonLazy;
    private SingletonLazy() {
    }
    public static SingletonLazy getInstance() {
        singletonLazy = new SingletonLazy();
        return singletonLazy;
    }
    public void showMessage() {
        System.out.println("Hello World! I`m Miss Lazy~");
    }
}

2.2.2 Lazy mode lock

This method has very good lazy loading and can work well in multi-threading, but it is very inefficient and does not require synchronization in 99% of cases.
Advantages: It is initialized only after the first call, avoiding memory waste.
Disadvantages: You must lock synchronized to ensure a singleton, but locking will affect efficiency.
The performance of getInstance() is not critical to the application (this method is used less frequently).


public class SingletonLazySynchronized {

    private static SingletonLazySynchronized singletonLazy;
    private SingletonLazySynchronized() {
    }
    public static synchronized SingletonLazySynchronized getInstance() {
        if (singletonLazy == null) {
            singletonLazy = new SingletonLazySynchronized();
        }
        return singletonLazy;
    }
    public void showMessage() {
        System.out.println("Hello World! I`m Miss Lazy Synchronized~");
    }
}

2.2.3 Reentrant lock in lazy mode

The implementation idea is the same as the synchronization lock:

public class SingletonLazyReentrantLock {
    private static SingletonLazyReentrantLock singletonLazyReentrantLock;
    private static ReentrantLock lock = new ReentrantLock();;

    private SingletonLazyReentrantLock() {
    }
    public static SingletonLazyReentrantLock getInstance() {
        lock.lock();
        if (null == singletonLazyReentrantLock) {
            singletonLazyReentrantLock = new SingletonLazyReentrantLock();
        }
        lock.unlock();
        return singletonLazyReentrantLock;
    }
    public void showMessage() {
        System.out.println("Hello World! I`m Miss Lazy ReentrantLock~");
    }
}

2.2.4 Double lock in lazy mode

This method uses a double lock mechanism, which is safe and maintains high performance in multi-threaded situations.
The performance of getInstance() is critical to the application.

public class SingletonLazyDoubleLock {
    private volatile static SingletonLazyDoubleLock singleton;
    private SingletonLazyDoubleLock() {
    }

    public static synchronized SingletonLazyDoubleLock getInstance() {
        if (singleton == null) {
            synchronized (SingletonLazyDoubleLock.class) {
                if (singleton == null) {
                    singleton = new SingletonLazyDoubleLock();
                }
            }
        }
        return singleton;
    }
    public void showMessage() {
        System.out.println("Hello World! I`m Miss Lazy DoubleLock~");
    }
}

2.2.5 Lazy-mode inner classes

This method can achieve the same effect as the double-check lock method, but the implementation is simpler. Use lazy initialization for static fields, which should be used instead of double-checked locking. This method is only suitable for static fields, and the double-check lock method can be used when the instance field needs to be initialized lazily.
This method also uses the classloder mechanism to ensure that there is only one thread when the instance is initialized. It is different from the hungry mode: as long as the Singleton class is loaded in the hungry mode, the singleton will be instantiated (without the lazy loading effect) , and this way the SingletonLazyInnerClass class is loaded, the singleton is not necessarily initialized. Because the SingletonLazyInnerClass class is not actively used, the SingletonLazyInnerClass class will be explicitly loaded only when the getInstance method is explicitly called to instantiate the singleton.
Imagine if instantiating a singleton consumes resources, so you want it to be loaded lazily, on the other hand, you don't want to instantiate when the singleton class is loaded, because there is no guarantee that the singleton class may also be actively used elsewhere Therefore, it is loaded, then instantiation at this time is obviously inappropriate. At this time, this method is very reasonable compared to the singleton.

public class SingletonLazyInnerClass {
    private static class SingletonHolder {
        private static final SingletonLazyInnerClass INSTANCE = new SingletonLazyInnerClass();
    }
    private SingletonLazyInnerClass() {
    }
    public static final SingletonLazyInnerClass getInstance() {
        return SingletonHolder.INSTANCE;
    }
    public void showMessage() {
        System.out.println("Hello World! I`m Miss Lazy InnerClass~");
    }
}

2.3 call

public class Client {
    public static void main(String[] args) {
        // 调用构造器会报错
        // Singleton s = new Singleton();

        // 调用其静态方法获取实例
        Singleton instance = Singleton.getInstance();
        instance.showMessage();
        // 调用其静态方法获取实例 懒加载 无锁
        SingletonLazy instanceLazy = SingletonLazy.getInstance();
        instanceLazy.showMessage();

        // 调用其静态方法获取实例 懒加载 加锁
        SingletonLazySynchronized instanceLazySynchronized = SingletonLazySynchronized.getInstance();
        instanceLazySynchronized.showMessage();

        // 调用其静态方法获取实例 懒加载 重入锁
        SingletonLazyReentrantLock instanceLazyReentrant = SingletonLazyReentrantLock.getInstance();
        instanceLazyReentrant.showMessage();

        // 调用其静态方法获取实例 懒加载 双锁机制
        SingletonLazyDoubleLock instanceLazyDcl = SingletonLazyDoubleLock.getInstance();
        instanceLazyDcl.showMessage();

        // 调用其静态方法获取实例 懒加载 内部类 类加载时单线程机制
        SingletonLazyInnerClass instanceLazyInner = SingletonLazyInnerClass.getInstance();
        instanceLazyInner.showMessage();
    }
}

result:

Hello World!
Hello World! I`m Miss Lazy~
Hello World! I`m Miss Lazy Synchronized~
Hello World! I`m Miss Lazy ReentrantLock~
Hello World! I`m Miss Lazy DoubleLock~
Hello World! I`m Miss Lazy InnerClass~

Code address: click to jump

References:
[1] Graphical Design Patterns/(Japanese) Hiroshi Yuki; translated by Yang Wenxuan. – Beijing: People’s Posts and Telecommunications Press, 2017.1.
[ 2 ] Wikipedia Design
Patterns [ 3 ] Geek Academy WIKI – Design Patterns .
[ 4 ] Rookie Tutorial – Design Patterns .

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325159450&siteId=291194637