阅读 77

设计模式——单例模式

设计模式——单例模式

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

饿汉式单例

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

// 饿汉式单例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表来实现单例在获取单例时还是会出现线程安全问题。

来源https://www.cnblogs.com/funtirn/p/15426201.html

文章分类
后端
文章标签
版权声明:本站是系统测试站点,无实际运营。本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 XXXXXXo@163.com 举报,一经查实,本站将立刻删除。
相关推荐