设计模式之单例传说


单例模式传说

定义

确保一个类只有一个实例,自行实例化并向系统提供这个实例,属于创建性模式。

分类

饿单例模式(类加载时实例化一个对象给自己的引用)

懒单例模式(调用取得实例的方法如getInstance时才会实例化对象)(java中饿单例模式性能优于懒单例模式,c++中一般使用懒单例模式)

登记式单例模式(用容器管理实例)

单例模式优点

在内存中只有一个对象,节省内存空间。
避免频繁的创建销毁对象,可以提高性能。
避免对共享资源的多重占用。

适用场景

需要频繁实例化然后销毁的对象。
创建对象时耗时过多或者耗资源过多,但又经常用到的对象。
有状态的工具类对象。
频繁访问数据库或文件的对象。

Java代码实现

1.懒汉式完美版

解决多线程访问时引发的问题

  • synchronized 效率低下的问题

解决在jit环境下指令重排引起的问题

  • 非原子操作会涉及到指令重排
public class SingletonStudy {
    private static volatile SingletonStudy singletonStudy = null;

    private SingletonStudy() {
    }

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

2.饿汉式完美版

解决了上述代码过于复杂的问题。

缺点:

  • 可能由于初始化的太早,造成资源的浪费
  • 如果初始化本身依赖于一些其他数据,那么也就很难保证其他数据会在它初始化之前准备好。
public class SingletonStudy {

    //这里加final是为了防止内部将这个属性覆盖掉
    private static final SingletonStudy singletonStudy = new SingletonStudy();

    private SingletonStudy() {

    }
    //这里加final是为了防止子类重写父类
    public static SingletonStudy getInstance() {
        return singletonStudy;
    }
}

3.懒汉饿汉结合版本

/*
 * 
 *	Effective Java 中的代码
 * 懒汉式和饿汉式可以完美结合吗?
 * 从外面看是懒汉式。里面确实饿汉。可以延迟加载资源,解决饿汉资源占用的问题。
 *
 *
 * 魔法: 通过ClassLoader来保证同步,使INSTANCE是一个真·单例。
 *       同时,由于SingletonHolder是一个内部类,
 *       只在外部类的Singleton的getInstance()中被使用,
 *       所以它被加载的时机也就是在getInstance()方法第一次被调用的时候。
 *
 * */
public class SingletonStudy {
    private static class SingletonHolder {

        private static final SingletonStudy INSTANCE = new SingletonStudy();

    }

    private SingletonStudy() {
    }

    public static final SingletonStudy getInstance() {

        return SingletonHolder.INSTANCE;
    }
}

4.枚举版

由于创建枚举对象的时候是线程安全的上述的所有因为并发引发问题都不复存在,所以不需要添加额外的代码

需要jdk1.5以上

public enum SingleInstance {
    INSTANCE;
    public void function(){

    }
}

5.登记式单例模式(支持继承)

以上1234均不支持继承

public class Singleton{
    //Spring最底层的这个容器就是一个map,说白了,IOC容器就是一个map
    private static Map<String, Singleton7> map = new ConcurrentHashMap<String, Singleton7>();
//每个class对应一个map的key,也就是唯一的id
static {
    Singleton7 singleton7 = new Singleton7();
    map.put(singleton7.getClass().getName(), singleton7);
}

//保护默认构造函数
protected Singleton7() {
}

//静态工厂方法,返回此类唯一的实例
public static Singleton7 getInstance(String name) {
    if (name == null) {
        name = Singleton7.class.getName();
    }
    if (map.get(name) == null) {
        try {
            map.put(name, (Singleton7) Class.forName(name).newInstance());
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
    return map.get(name);
}
}

文章作者: Bxan
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 Bxan !
  目录