Design pattern--Singleton mode Singleton

singleton design pattern

Singleton design pattern: Take a certain method to ensure that in the entire software system, there can only be one object instance for a certain class , and this class only provides a static method to obtain its object instance.

Eight ways of singleton design pattern:

  1. Hungry style (static constant)
  2. Hungry Chinese style (static code block)
  3. Lazy (not thread safe)
  4. Lazy (thread-safe, synchronized methods)
  5. Lazy (thread unsafe, synchronized code blocks)
  6. double check
  7. static inner class
  8. enumerate

1. Hungry style (static constant)

"Hungry man style", that is, you can try to think about it, the behavior of a hungry man must be to eat food when he sees it. That is, no matter whether the instance object is needed or not, a copy will be automatically loaded in memory.

Explanation of advantages and disadvantages:

  1. Advantages: The writing method is simple to implement, and the instantiation is completed when the class is loaded. The problem of thread synchronization is avoided.
  2. Disadvantage: The instantiation is completed when the class is loaded. If this instance has not been used, it will cause a waste of memory resources.

This singleton mode can be used, which may cause a waste of resources.

Code:

package com.robin.singleton.type1;

public class SingletonTest1 {
    
    
    public static void main(String[] args) {
    
    
        // 测试单例模式--饿汉式(静态常量)
        Singleton instance1 = Singleton.getInstance();
        Singleton instance2 = Singleton.getInstance();
        System.out.println("instance1======"+instance1.hashCode());
        System.out.println("instance2======"+instance2.hashCode());
        // instance1 与 instance2 哈希值一致,是同一个对象实例
    }
}

// 单例模式----饿汉式(静态常量)

class Singleton{
    
    
    // 私有构造器,防止外部以 new 的方式创建对象
    private Singleton(){
    
    }

    // 类加载时,即创建唯一的一份对象
    private final static Singleton instance = new Singleton();

    // 对外提供一个公共的返回 singleton 实例对象
    public static Singleton getInstance(){
    
    
        return instance;
    }
}

2. Hungry style (static code block)

Similar to the first method above, except that the process of class instantiation is placed in a static code block, the advantages and disadvantages are the same as above.

Code:

package com.robin.singleton.type2;

public class SingletonTest2 {
    
    
    public static void main(String[] args) {
    
    
        // 测试单例模式--饿汉式(静态代码块)
        Singleton instance1 = Singleton.getInstance();
        Singleton instance2 = Singleton.getInstance();
        System.out.println("instance1======"+instance1.hashCode());
        System.out.println("instance2======"+instance2.hashCode());
        // instance1 与 instance2 哈希值一致,是同一个对象实例
    }
}

// 单例模式----饿汉式(静态代码块)

class Singleton{
    
    
    // 私有构造器,防止外部以 new 的方式创建对象
    private Singleton(){
    
    }

    private static Singleton instance ;

    // 在静态代码块中-->类加载时,完成instance的初始化
    static {
    
    
        instance = new Singleton();
    }

    // 对外提供一个公共的返回 singleton 实例对象
    public static Singleton getInstance(){
    
    
        return instance;
    }
}

3. Lazy style (thread unsafe)

"Lazy style", eat when you are very hungry. That is, when needed, the class instance object is created and loaded into memory.

Advantages and disadvantages:

  1. Lazy loading is implemented, but it can only be used under a single thread.
  2. Under multi-threading, multiple instances will be generated.
  3. In actual development, do not use this method.

Code:

package com.robin.singleton.type3;

public class SingletonTest3 {
    
    
    public static void main(String[] args) {
    
    
        // 测试单例模式--懒汉式(线程不安全)
        Singleton instance1 = Singleton.getInstance();
        Singleton instance2 = Singleton.getInstance();
        System.out.println("instance1======"+instance1.hashCode());
        System.out.println("instance2======"+instance2.hashCode());
        // instance1 与 instance2 哈希值一致,是同一个对象实例

    }

}

// 单例模式----饿汉式(静态代码块)

class Singleton {
    
    
    // 私有构造器,防止外部以 new 的方式创建对象
    private Singleton() {
    
    
    }

    private static Singleton instance;


    // 当使用到时,才去初始化实例对象(懒汉式)
    // 对外提供一个公共的返回 singleton 实例对象
    public static Singleton getInstance() {
    
    
        if (instance == null) {
    
    
            instance = new Singleton();
        }
        return instance;
    }
}

Multithreaded test:

package com.robin.singleton.type3.threadSingleton;

public class TestThreadSingleton {
    
    
    public static void main(String[] args) {
    
    
        System.out.println("~~~~~多线程测试~~~~~");
        // 多线程测试懒汉式的线程不安全的写法
        for (int i = 0; i < 100; i++) {
    
    
            // 匿名内部类+lambda表达式
            new Thread(() -> {
    
    
                System.out.println(Singleton.getInstance().hashCode());
            }).start();
        }
        // hashCode 都乱了。线程不安全
    }
}

// 懒汉式(线程不安全)
class Singleton{
    
    
    // 私有构造器
    private Singleton(){
    
    }

    private static Singleton instance;

    // 懒汉式,当需要时进行加载(调用getInstance()方法)获得实例对象
    // 提供对外公共的 getInstance()
    public static Singleton getInstance(){
    
    
        if (instance==null){
    
    
            // 线程睡眠1s进行测试
            try {
    
    
                Thread.sleep(1);
            } catch (InterruptedException e) {
    
    
                e.printStackTrace();
            }
            instance = new Singleton();
        }
        return instance;
    }
}

insert image description here

4. Lazy style (thread-safe, synchronized methods)

Code:

package com.robin.singleton.type4;

public class SingletonTest4 {
    
    
    public static void main(String[] args) {
    
    
        // 测试单例模式--懒汉式(线程安全)
        Singleton instance1 = Singleton.getInstance();
        Singleton instance2 = Singleton.getInstance();
        System.out.println("instance1======"+instance1.hashCode());
        System.out.println("instance2======"+instance2.hashCode());
        // instance1 与 instance2 哈希值一致,是同一个对象实例
    }
}

// 单例模式----懒汉式(线程安全)
class Singleton{
    
    
    // 私有构造器,防止外部以 new 的方式创建对象
    private Singleton(){
    
    }

    private static Singleton instance ;

    // 对外提供一个公共的返回 singleton 实例对象的方法getInstance()
    // 使用同步方法 synchronized 线程同步锁
    public static synchronized Singleton getInstance(){
    
    
        if (instance==null){
    
    
            instance = new Singleton();
        }
        return instance;
    }
}

Thread test:

package com.robin.singleton.type4.threadSingleton;

public class TestThreadSingleton {
    
    
    public static void main(String[] args) {
    
    
        System.out.println("多线程测试【懒汉式单例模式】");
        for (int i = 0; i < 100; i++) {
    
    
            new Thread(()->{
    
    
                System.out.println(Singleton.getInstance().hashCode());
            }).start();
        }
        // 哈希值没有乱,线程安全
        // 但是同步锁 synchronized 比较耗费时间,效率不高
    }
}

// 懒汉式(线程安全)
class Singleton{
    
    

    // 私有化构造器
    private Singleton(){
    
    }
    // 静态变量
    private static Singleton instance;
    // 向外提供getInstance()方法,需要时才创建
    // synchronized 线程同步锁
    public static synchronized Singleton getInstance(){
    
    
        if (instance==null){
    
    
            // 为了线程测试,线程中断休眠1s
            try {
    
    
                Thread.sleep(1);
            } catch (InterruptedException e) {
    
    
                e.printStackTrace();
            }
            instance = new Singleton();
        }
        return instance;
    }
}

insert image description here

Advantages and disadvantages:

  1. Advantages: The multi-thread security problem is solved by the synchronized synchronization method
  2. Disadvantages: low efficiency, because of the problem of synchronization locks, locks are required every time, regardless of whether the instance exists or not

In actual development, this usage is not recommended.

5. Lazy style (thread unsafe, synchronized code blocks)

Code:

package com.robin.singleton.type5;

public class SingletonTest5 {
    
    
    public static void main(String[] args) {
    
    
        // 测试单例模式--懒汉式(线程不安全)
        Singleton instance1 = Singleton.getInstance();
        Singleton instance2 = Singleton.getInstance();
        System.out.println("instance1======"+instance1.hashCode());
        System.out.println("instance2======"+instance2.hashCode());
        // instance1 与 instance2 哈希值一致,是同一个对象实例
    }
}

// 单例模式----懒汉式(线程不安全,妄图通过减小同步代码块的方式来提高效率)
class Singleton{
    
    
    // 私有构造器,防止外部以 new 的方式创建对象
    private Singleton(){
    
    }

    private static Singleton instance ;

    // 对外提供一个公共的返回 singleton 实例对象的方法getInstance()
    public static  Singleton getInstance(){
    
    
        if (instance==null){
    
    
            // 妄图通过减小同步代码块的方式来提高效率,会导致线程不安全
            synchronized(Singleton.class){
    
    
                instance = new Singleton();
            }
        }
        return instance;
    }
}

Multithreaded test:

package com.robin.singleton.type5.threadSingleton;

public class SingletonTest5 {
    
    
    public static void main(String[] args) {
    
    
        // 测试单例模式--懒汉式(线程不安全)
        for (int i = 0; i < 100; i++) {
    
    
            new Thread(()->{
    
    
                System.out.println(Singleton.getInstance().hashCode());
            }).start();
        }
    }
}

// 单例模式----懒汉式(线程不安全,妄图通过减小同步代码块的方式来提高效率)
class Singleton{
    
    
    // 私有构造器,防止外部以 new 的方式创建对象
    private Singleton(){
    
    }

    private static Singleton instance ;

    // 对外提供一个公共的返回 singleton 实例对象的方法getInstance()
    public static  Singleton getInstance(){
    
    
        if (instance==null){
    
    
            // 妄图通过减小同步代码块的方式来提高效率,会导致线程不安全
            synchronized(Singleton.class){
    
    
                // 线程休眠1s
                try {
    
    
                    Thread.sleep(1);
                } catch (InterruptedException e) {
    
    
                    e.printStackTrace();
                }
                instance = new Singleton();
            }

        }
        return instance;
    }
}

insert image description here

Trying to improve efficiency by reducing the number of synchronous code blocks, but it will lead to multi-threading or security issues, it can only be used under single-threaded, not recommended.

6. Double check

Thread safety is guaranteed by double checking, twice if(singleton==null). The instantiation code is only executed once, avoiding repeated method synchronization.

Code:

package com.robin.singleton.type6;

public class SingletonTest6 {
    
    
    public static void main(String[] args) {
    
    
        // 测试单例模式--双重检查
        Singleton instance1 = Singleton.getInstance();
        Singleton instance2 = Singleton.getInstance();
        System.out.println("instance1======"+instance1.hashCode());
        System.out.println("instance2======"+instance2.hashCode());
    }
}

// 单例模式--双重检查
class Singleton{
    
    
    private Singleton(){
    
    };

    // 使用 volatile 关键字
    private static volatile Singleton instance;

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

}

Solve the security problem of multi-threading, double check can solve the problem of lazy loading, that is, it will be loaded when it is used (to avoid wasting memory resources), and at the same time ensure the efficiency, it is recommended to use.

7. Static Inner Classes

Static inner classes are only instantiated when they are used, avoiding waste of resources and implementing lazy loading. At the same time, the JVM guarantees the safety of the thread. When the class is initialized, only one thread can enter.

Code:

package com.robin.singleton.type7;

public class SingletonTest7 {
    
    
    public static void main(String[] args) {
    
    
        // 测试单例模式--静态内部类的方式
        Singleton instance1 = Singleton.getInstance();
        Singleton instance2 = Singleton.getInstance();
        System.out.println("instance1======"+instance1.hashCode());
        System.out.println("instance2======"+instance2.hashCode());
    }
}

// 单例模式--静态内部类的方式
class Singleton{
    
    
    private Singleton(){
    
    };

    // 私有静态内部类
    private static class SingletonInstance{
    
    
        private static final Singleton INSTANCE =new Singleton();
    }

    // 提供公有的外部获取实例的方法 getInstance()
    public static Singleton getInstance(){
    
    
        return SingletonInstance.INSTANCE;
    }

}

It avoids thread insecurity, and uses the characteristics of static inner classes to implement lazy loading, which is highly efficient.

+1 for the recommendation!

8. Enumeration

Using enumeration to implement singleton mode can not only avoid multi-thread synchronization problems, but also prevent deserialization from recreating new objects. +1 for the recommendation!

Code:

package com.robin.singleton.type8;

public class SingletonTest8 {
    
    
    public static void main(String[] args) {
    
    
        // 测试单例模式--枚举的方式
        Singleton instance1 = Singleton.INSTANCE;
        Singleton instance2 = Singleton.INSTANCE;
        System.out.println(instance1==instance2);
        System.out.println("instance1===="+instance1.hashCode());
        System.out.println("instance2===="+instance2.hashCode());
    }
}

// 单例模式--枚举的方式
enum Singleton{
    
    
    INSTANCE;
}

Guess you like

Origin blog.csdn.net/m0_63622279/article/details/128933666