Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

单例模式 #40

Open
HealUP opened this issue Jul 10, 2023 · 1 comment
Open

单例模式 #40

HealUP opened this issue Jul 10, 2023 · 1 comment

Comments

@HealUP
Copy link
Owner

HealUP commented Jul 10, 2023

单例模式类图展示:
image.png

单例模式就是一个类只能有一个对象
使用场景:

  • 你希望这个类只有一个且只能有一个实例;
  • 项目中的一些全局管理类(Manager)可以用单例来实现。

下面要介绍的几种单例模式的实现方式
image.png

饿汉式
步骤一:

public class SingleObject {
    private static SingleObject instance = new SingleObject();
    
    // 构造函数为private,避免被实例化
    private SingleObject(){};
    
    // 获取唯一可用的对象
    public static SingleObject getInstance() {
        return instance;
    }
    
    public showMessage(){
        System.out.println("hello guy!");
    }
}

步骤二:

public class SingleObjectDemo {
    public static void main(String[] args) {
        // 获取唯一可用的对象
        SingleObject object = SingleObject.getInstance();

        // 调用方法
        object.showMessage();
    }
}

步骤三:
hello guy!

以上就是饿汉式的实现方式,优点是没有加锁,执行效率高;支持多线程(利用的是JVM的类加载机制,在类初始化时就已经被加载,保证同一时刻只有一个线程获取到实例)
ps:这种方式是最常用的,但是容易产生垃圾对象

懒汉式-线程安全

public class SingleObject {
    private static SingleObject instance;

    private SingleObject(){};

    public static synchronized  SingleObject getInstance() {
        if (instance = null) {
            instance = new SingleObject();
        }
        return instance;
    }
}

ps:优点是支持多线程,lazy加载(一开始先不实例化可以避免内存的浪费);必须使用但是使用synchronized才能保证单例,但是加了加锁操作,效率比较低,99%情况下是不需要同步的,不推荐使用。

懒汉式—线程不安全

public class SingleObject {
    private static SingleObject instance;

    private SingleObject(){};

    public static SingleObject getInstance() {
        if (instance = null) {
            instance = new SingleObject();
        }
        return instance;
    }
}

ps:这种方式是最基本的实现方式,这种实现最大的问题就是不支持多线程。因为没有加锁 synchronized,所以严格意义上它并不算单例模式。

双检锁(DCL,即 double-checked locking)

public class Singleton {  
    private volatile static Singleton singleton;  
    private Singleton (){}  
    public static Singleton getSingleton() {  
    if (singleton == null) {  
        synchronized (Singleton.class) {  
            if (singleton == null) {  
                singleton = new Singleton();  
            }  
        }  
    }  
    return singleton;  
    }  
}

ps:多了volatile,同时,又用了synchronized,这种方式采用双锁机制,安全且在多线程情况下能保持高性能。
使用场景:getInstance() 的性能对应用程序很关键。

登记式

public class Singleton {
    private static class SingletonHolder{
        private static final Singleton INSTANCE = new Singleton();
    }

    private Singleton(){};
    public static final Singleton getInstance() {
        return SingletonHolder.INSTANCE;
    }
}

PS:使用了静态内部类,这种方式能达到双检锁方式一样的功效,但实现更简单。对静态域使用延迟初始化,应使用这种方式而不是双检锁方式。这种方式只适用于静态域的情况,双检锁方式可在实例域需要延迟初始化时使用。

解释:这种方式同样利用了 classloader 机制来保证初始化 instance 时只有一个线程,它跟饿汉式方式不同的是:饿汉式只要 Singleton 类被装载了,那么 instance 就会被实例化(没有达到 lazy loading 效果);而这种方式是 Singleton 类被装载了,instance 不一定被初始化。因为 SingletonHolder 类没有被主动使用,只有通过显式调用 getInstance 方法时,才会显式装载 SingletonHolder 类,从而实例化 instance。

使用场景:如果实例化 instance 很消耗资源,所以想让它延迟加载,另外一方面,又不希望在 Singleton 类加载时就实例化,因为不能确保 Singleton 类还可能在其他的地方被主动使用从而被加载,那么这个时候实例化 instance 显然是不合适的。这个时候,这种方式相比饿汉式就显得比较合理

枚举

public enum Singleton {  
    INSTANCE;  
    public void whateverMethod() {  
    }  
}

总结::一般情况下,不建议使用懒汉方式,建议使用饿汉方式(线程安全,但没有懒加载)。只有在要明确实现 lazy loading 效果时,才会使用登记方式。如果涉及到反序列化创建对象时,可以尝试使用枚举方式。如果有其他特殊的需求,可以考虑使用双检锁方式。

@HealUP
Copy link
Owner Author

HealUP commented Jul 10, 2023

早起的一天冲!

@HealUP HealUP added 置顶文章 and removed TOP work well labels Jul 10, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

1 participant