单例模式 学习

2021-01-20  本文已影响0人  fdsun

单例模式

单例模式(Singleton Pattern)是指确保一个类在任何情况下都绝对只有一个实例,并提供一个全局访问点。
隐藏其所有的构造方法。
属于创建型模式。

适用场景:

确保任何情况下都绝对只有一个实例。

单例模式的优点:

在内存中只有一个实例,减少了内存开销。
可以避免对资源的多重占用。
设置全局访问点,严格控制访问。

单例模式的缺点:

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

单例模式的常见写法:

1、饿汉式单例
2、懒汉式单例
3、注册式单例
4、ThreadLocal单例

饿汉式单例:

在单例类首次加载时就创建实例
缺点:浪费内存空间

饿汉式单例
public class HungrySingleton {
    private static final HungrySingleton hungrySingleton = new HungrySingleton();
    private HungrySingleton() {
    }
    public static HungrySingleton getInstance() {
        return hungrySingleton;
    }
}
/**
 * 饿汉式静态块单例
 */
public class HungryStaticSingleton {
    private static final HungryStaticSingleton hungrySingleton;
        static {
        hungrySingleton = new HungryStaticSingleton();
    }
    private HungryStaticSingleton() {
    }
    public static HungryStaticSingleton getInstance() {
        return hungrySingleton;
    }
}

懒汉式单例:

被外部类调用时才创建实例

懒汉式单例
public class LazySimpleSingleton {
    private LazySimpleSingleton() {
    }
    private static LazySimpleSingleton lazy = null;
    // 存在性能问题  -> DoubleCheck
    public synchronized static LazySimpleSingleton getInstance() {
        if (lazy == null) {
            lazy = new LazySimpleSingleton();
        }
        return lazy;
    }
}
public class LazyDoubleCheckSingleton {
    private volatile static LazyDoubleCheckSingleton lazy = null;

    private LazyDoubleCheckSingleton() {
    }
    // 适中的方案
    // 双重检查锁
    public static LazyDoubleCheckSingleton getInstance() {
        if (lazy == null) {
            synchronized (LazyDoubleCheckSingleton.class) {
                if (lazy == null) {
                    lazy = new LazyDoubleCheckSingleton();
                    //  cpu执行时候会转换成JVM指令执行
                    // 指令重排序的问题, volatile
                    //1.分配内存给这个对象
                    //2.初始化对象
                    //3.设置lazy指向刚分配的内存地址
                    //4.初次访问对象
                }
            }
        }
        return lazy;
    }
}
/**
 * 懒汉式单例
 * 这种形式兼顾饿汉式的内存浪费,也兼顾synchronized性能问题
 * 完美地屏蔽了这两个缺点
 * <p>
 * 全程没有用到synchronized
 * 性能最优的一种写法
 */
public class LazyInnerClassSingleton {
        private LazyInnerClassSingleton() {
        // 防止使用反射恶意调用,破坏单例
        if (LazyHolder.LAZY != null) {
            throw new RuntimeException("不允许创建多个实例");
        }
    }

    /**
     * 懒汉式单例
     * LazyHolder 里面的逻辑需要等到外部方法调用时才执行
     * 巧妙利用了内部类的特性
     * JVM底层独行逻辑,完美地避免了线程安全阿题
     */
    public static final LazyInnerClassSingleton getInstance() {
        //在返回结果以前,一定会先加载内部类
        return LazyHolder.LAZY;
    }

    // 默认不加载
    private static class LazyHolder {
        private static final LazyInnerClassSingleton LAZY = new LazyInnerClassSingleton();
    }
}

注册式单例:

将每一个实例都缓存到统一的容器中,使用唯一标识获取实例

注册式单例
/**
 * 枚举单例
 */
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) {
        synchronized (ioc) {
            if (!ioc.containsKey(className)) {
                Object obj = null;
                try {
                    obj = Class.forName(className).newInstance();
                    ioc.put(className, obj);
                } catch (Exception e) {
                    e.printStackTrace();
                }
                return obj;
            } else {
                return ioc.get(className);
            }
        }
    }
}

ThreadLocal单例:

保证线程内部的全局唯一,且天生线程安全

ThreadLocal单例
public class ThreadLocalSingleton {
    private static final ThreadLocal<ThreadLocalSingleton> THREAD_LOCAL_INSTANCE =
            ThreadLocal.withInitial(ThreadLocalSingleton::new);

    private ThreadLocalSingleton() {
    }

    public static ThreadLocalSingleton getInstance() {
        return THREAD_LOCAL_INSTANCE.get();
    }
}

学习单例模式的知识重点总结:

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

上一篇 下一篇

猜你喜欢

热点阅读