设计模式——单例模式

发布时间:2022-07-02 发布网站:脚本宝典
脚本宝典收集整理的这篇文章主要介绍了设计模式——单例模式脚本宝典觉得挺不错的,现在分享给大家,也给大家做个参考。

设计模式——单例模式

单例模式,顾名思义就是一个类只能有一个实例。单例模式根据实例的创建的时间大致可以分为两类——饿汉式单例和懒汉式单例。

饿汉式单例

饿汉式单例,是指在类初始化的时候就创建实例,这样做有一个好处,就是保证在获取实例的时候可以保证线程安全而且还简单,即多个线程获取到的都是同一个实例。但这样做也有一个缺点,就是即使不用实例,实例也会创建,这样就会造成内存浪费。饿汉式单例的简单实现:

// 饿汉式单例
class HungrySingleton {
    PRivate static final HungrySingleton instance = new HungrySingleton();

    private HungrySingleton() {

    }

    public static HungrySingleton getInstance() {
        return instance;
    }
}

懒汉式单例

懒汉式单例,见名知意,只有在需要的时候才会创建实例,直接看代码:

// 懒汉式单例
class LazySingleton {
    private static LazySingleton singleton;

    private LazySingleton() {

    }

    public LazySingleton getInstance() {
        if (singleton == null) {
            singleton = new LazySingleton();
        }
        return singleton;
    }
}

很显然懒汉式单例解决了,饿汉式单例可能出现的占用内存的情况,但是饿汉式单例同样带来了获取单例时线程不安全的问题,即可能出现多个线程取到的实例不是同一个,最直接的解决方案就是加锁。

class SynchronizedLazySingleton {
    private static SynchronizedLazySingleton singleton;

    private SynchronizedLazySingleton() {

    }

    public synchronized SynchronizedLazySingleton getInstance() {
        if (singleton == null) {
            singleton = new SynchronizedLazySingleton();
        }
        return singleton;
    }
}

但是加锁后会使代码性能变差。所以我们需要对上面的代码做个优化,采用volatile和synchronized配合的方式:

class DoubleLockSingleton {
    // 通过volatile修饰来确保singleton的状态改变在所有线程间可见
    volatile private static DoubleLockSingleton singleton;

    private DoubleLockSingleton() {

    }

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

但是这样对性能的提升有限,我们可以换一个思路,通过内部类的初始化来优化代码

class InnerClassSingleton {
    private InnerClassSingleton() {

    }

    // 静态内部类只有在使用的时候才会初始化
    public static InnerClassSingleton getInstance() {
        return SingletonHolder.singleton;
    }

    private static class SingletonHolder {
        private static final InnerClassSingleton singleton = new InnerClassSingleton();
    }
}

这样就可以达到性能与线程安全的平衡。

注册式单例模式

序列化会使单例失效:有时我们需要将单例序列化后写入磁盘,但是一旦从磁盘中把单例反序列化出来,由于单例的内存地址变了,这样实际上就是创建了一个新的实例,于是只有一个实例的原则就被破坏了。通过枚举类的特性来实现注册式单例:

enum EnumSingleton {
    SINGLETON;
    
    public EnumSingleton getInstance() {
        return SINGLETON;
    }
}

通过枚举类来实现单例模式,可以保证序列化后得到的是同一个对象,而且枚举类的实例不能通过反射来创建。同样我们也可以通过hash表来实现注册式单例:

class HashMapSingleton{
    private static final ConcurrentHashMap<String, Object> map = new ConcurrentHashMap<>();

    private HashMapSingleton(){

    }

    public static Object getInstance(String classname) {
        if (!map.containsKey(className)) {
            Object obj = null;
            try {
                obj = Class.forName(className).newInstance();
                map.put(className, obj);
            } catch (Exception e) {
                e.printStackTrace();
            }
            return obj;
        }
        return map.get(className);

    }
}

但是通过hash表来实现单例在获取单例时还是会出现线程安全问题。

脚本宝典总结

以上是脚本宝典为你收集整理的设计模式——单例模式全部内容,希望文章能够帮你解决设计模式——单例模式所遇到的问题。

如果觉得脚本宝典网站内容还不错,欢迎将脚本宝典推荐好友。

本图文内容来源于网友网络收集整理提供,作为学习参考使用,版权属于原作者。
如您有任何意见或建议可联系处理。小编QQ:384754419,请注明来意。