单例模式看了就够了

1、饿汉式单例
它是在类加载的时候就立即初始化,并且创建单例对象
优点:没有加任何的锁、执行效率比较高,在用户体验上来说,比懒汉式更好
缺点:类加载的时候就初始化,不管你用还是不用,我都占着空间,浪费了内存,有可能占着茅坑不拉屎

绝对线程安全,在线程还没出现以前就是实例化了,不可能存在访问安全问题

注意代码中的:readResolve方法及注释内容

public class HungrySingleton {
    
    private static final HungrySingleton hungrySingleton = new HungrySingleton();

    private HungrySingleton() {}

    //提供全局访问点
    public static HungrySingleton getInstance() {
        return hungrySingleton;
    }
    //防止通过序列化和反序列化的方式破坏单例
    private  Object readResolve(){
        return  INSTANCE;
    }
}

一上来就把单例对象创建出来了,要用的时候直接返回即可,这种可以说是单例模式中最简单的一种实现方式。但是问题也比较明显。单例在还没有使用到的时候,初始化就已经完成了。也就是说,如果程序从头到位都没用使用这个单例的话,单例的对象还是会创建。这就造成了不必要的资源浪费。所以不推荐这种实现方式。

1.1 饿汉式静态块单例

public class HungryStaticSingleton {
    private static final HungryStaticSingleton hungrySingleton;
    static {
        hungrySingleton = new HungryStaticSingleton();
    }
    private HungryStaticSingleton(){}
    public static HungryStaticSingleton getInstance(){
        return  hungrySingleton;
    }
}

2.懒汉式单例(在外部需要使用的时候才进行实例化)

public class LazySimpleSingleton {
    //构造器私有化
    private LazySimpleSingleton(){}
    //静态块,公共内存区域
    private static LazySimpleSingleton lazy = null;

    public synchronized static LazySimpleSingleton getInstance(){
        if(lazy == null){
            lazy = new LazySimpleSingleton();
        }
        return lazy;
    }
}

3.Double CheckLock实现单例:DCL也就是双重锁判断机制(由于JVM底层模型原因,偶尔会出问题,不建议使用):

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();
                }
            }
        }
        return lazy;
    }
}

4.静态内部类实现模式(线程安全,调用效率高,可以延时加载)

public class LazyInnerClassSingleton {
    //默认使用LazyInnerClassSingleton的时候,会先初始化内部类
    //如果没使用的话,内部类是不加载的
    private LazyInnerClassSingleton(){
        //加if判断,是为了防止通过反射的方式获取实例,而造成单例被破坏。也就是防止反射攻击
        if(LazyHolder.LAZY != null){
            throw new RuntimeException("不允许创建多个实例");
        }
    }

    //每一个关键字都不是多余的
    //static 是为了使单例的空间共享
    //final 保证这个方法不会被重写,重载
    public static final LazyInnerClassSingleton getInstance(){
        //在返回结果以前,一定会先加载内部类
        return LazyHolder.LAZY;
    }

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

 

5.枚举类(线程安全,调用效率高,不能延时加载,JDK层面天然的防止反射和反序列化调用)

JDK层面就不允许反射方式ENUM创建对象,可以查看jdk源码

枚举的单例模式使用我们的单例更加的优雅

public enum SingletonDemo5 {
     
    //枚举元素本身就是单例
    INSTANCE;
     
    //添加自己需要的操作
    public void singletonOperation(){     
    }
}

6.注册式单例

注册式单例(容器式),保证线程内部的全局唯一,使用场景:多数据源动态切换。

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();
    }
}

单例模式总结:

1,私有化构造器

2,保证线程安全

3,延迟加载

4,防止序列化和反序列化破坏单例

5,防御反射攻击单例

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

 

如何选用:

-单例对象 占用资源少,不需要延时加载,枚举 好于 饿汉

-单例对象 占用资源多,需要延时加载,静态内部类 好于 懒汉式

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章