一. 类型

创建型模式

二. 优点

  1. 保证一个类仅有一个实例,减少了内存开销.
  2. 可以避免对资源的多重占用.
  3. 设置全局访问点, 严格控制访问.

三. 缺点

  1. 没有接口, 扩展困难.
  2. 如果要扩展单例对象, 只有修改代码, 无其他途径.

四. 单例的相关注意点

  1. 私有化构造器
  2. 保证线程安全
  3. 延迟加载
  4. 防止序列化和反序列化破坏单例
  5. 防御反射破攻击单例

五. 多种单例写法

饿汉式单例

/**
 * 优点:执行效率高,性能高,没有任何的锁
 * 缺点:由于提前创建对象, 没有使用的情况下,可能会造成内存浪费
 */
public class HungrySingleton {

    private static final HungrySingleton hungrySingleton = new HungrySingleton();

    private HungrySingleton(){}

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

懒汉式单例(延迟加载)

简单写法

/**
 * 优点:节省了内存,线程安全
 * 缺点:性能低
 */
public class LazySimpleSingletion {
    private static LazySimpleSingletion instance;
    private LazySimpleSingletion(){}

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

DCL 双重检查锁写法

/**
 * 优点:性能高了,线程安全了
 * 缺点:可读性难度加大,不够优雅
 */
public class LazyDoubleCheckSingleton {
    private volatile static LazyDoubleCheckSingleton instance;
    private LazyDoubleCheckSingleton(){}

    public static LazyDoubleCheckSingleton getInstance(){
        //检查是否要阻塞
        if (instance == null) {
            synchronized (LazyDoubleCheckSingleton.class) {
                //检查是否要重新创建实例
                if (instance == null) {
                    instance = new LazyDoubleCheckSingleton();
                    //指令重排序的问题
                }
            }
        }
        return instance;
    }
}

静态内部类写法

/**
  静态内部类写法
  ClassPath : LazyStaticInnerClassSingleton.class
              LazyStaticInnerClassSingleton$LazyHolder.class
   优点:利用了Java本身语法特点,性能高,避免了内存浪费,不能被反射破坏
   缺点:不优雅
 */
public class LazyStaticInnerClassSingleton {

    private LazyStaticInnerClassSingleton(){
        // 此处防止反射攻击破坏单例所做的检查, 此处让代码变得不优雅, 且不易懂
        if(LazyHolder.INSTANCE != null){
            throw new RuntimeException("不允许非法访问");
        }
    }

    private static LazyStaticInnerClassSingleton getInstance(){
        return LazyHolder.INSTANCE;
    }

    private static class LazyHolder{
        private static final LazyStaticInnerClassSingleton INSTANCE = new LazyStaticInnerClassSingleton();
    }

}

注册式单例

枚举式单例

/**
 * 枚举式单例
 * 优点: 优雅的写法
 * 缺点: 不能大批量创建对象
 */
public enum EnumSingleton {
    INSTANCE;

    private Object data;

    public Object getData() {
        return data;
    }

    public void setData(Object data) {
        this.data = data;
    }

    public static EnumSingleton getInstance(){
        return INSTANCE;
    }
}

容器式单例

/**
 * 容器式单例
 * 基于枚举式单例的改进, 可以大批量创建对象,
 * 以下代码需要增加线程安全的逻辑
 */
public class ContainerSingleton {

    private ContainerSingleton(){}

    private static Map<String,Object> ioc = new ConcurrentHashMap<String, Object>();

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

}

解决序列化的单例

public class SeriableSingleton implements Serializable {

    //序列化
    //把内存中对象的状态转换为字节码的形式
    //把字节码通过IO输出流,写到磁盘上
    //永久保存下来,持久化

    //反序列化
    //将持久化的字节码内容,通过IO输入流读到内存中来
    //转化成一个Java对象

    public  final static SeriableSingleton INSTANCE = new SeriableSingleton();
    private SeriableSingleton(){}

    public static SeriableSingleton getInstance(){
        return INSTANCE;
    }

    // 反序列化时, 输入流对象底层实际上是new的一个新对象返回
    // 如果有该方法的话, 输入流会通过反射找到该名称的方法, 获取到返回结果来避免new一个对象
    private Object readResolve(){
        return INSTANCE;
    }

}

ThreadLoocal 式单例

public class ThreadLocalSingleton {
    private static final ThreadLocal<ThreadLocalSingleton> threadLocaLInstance =
            new ThreadLocal<ThreadLocalSingleton>(){
                @Override
                protected ThreadLocalSingleton initialValue() {
                    return new ThreadLocalSingleton();
                }
            };

    private ThreadLocalSingleton(){}

    public static ThreadLocalSingleton getInstance(){
        return threadLocaLInstance.get();
    }
}
最后修改:2022 年 06 月 23 日
如果觉得我的文章对你有用,请点个赞吧~