美文网首页
设计模式之单例的实现

设计模式之单例的实现

作者: 得鹿梦为鱼 | 来源:发表于2020-02-25 22:38 被阅读0次

    单例模式

    一个类只能有一个实例

    为什么使用单例?

    节省内存、节省计算 、方便管理
    Spring 管理的bean默认就是单例

    常见的单例模式

    饿汉模式
    饱汉/懒汉模式
    双重检查,模式
    静态内部类模式
    枚举模式

    恶汉模式

    缺点: 在类装载时就完成了实例化,没有达到懒加载的效果,如果从始至终都没有使用,那么会存在内存浪费的问题
    优点: 在类装载时就完成了实例化,避免了线程同步问题

     /**
     * 单例-饿汉模式<br>
     */
    public class Hungry {
    
        private static Hungry instance = new Hungry();
    
        private Hungry() {
    
        }
    
        public static Hungry getInstance() {
            return instance;
        }
    }
    
    

    静态代码块模式,优缺点与上面是一样的

    /**
     * 单例- 恶汉 - 静态代码块模式<br> 
     */
    public class Hungry2Static {
    
        private static Hungry2Static instance;
    
        static {
            instance = new Hungry2Static();
        }
    
        private Hungry2Static() {
        }
    
        public static Hungry2Static getInstance() {
            return instance;
        }
    }
    

    饱汉/懒汉模式

    在调用 getInstance 时才初始化,避免了内存浪费的问题
    但这种模式只能在单线程下使用

    在多线程模式下,如果一个线程进入了 if (instance == null) ,还没来得及进入下一步,另一个线程也进来了,就会存在创建多个实例的情况

    /**
     * 单例 - 饱汉/懒汉模式<br>
     */
    public class Lazy {
        private Lazy() {
        }
    
        private static Lazy instance;
    
        public static Lazy getInstance() {
            if (instance == null) {
                instance = new Lazy();
            }
            return instance;
        }
    }
    
    升级

    给 getInstance 加上 synchronized 关键字
    优点:保证了多线程下线程安全问题
    缺点:调用方法是阻塞性的,性能开销大,效率太低

    /**
     * 单例 - 饱汉/懒汉模式<br>
     */
    public class Lazy {
        private Lazy() {
        }
    
        private static Lazy instance;
    
        public synchronized static Lazy getInstance() {
            if (instance == null) {
                instance = new Lazy();
            }
            return instance;
        }
    
    }
    

    为了调高效果,所以缩小同步范围

    将 synchronized 关键字从 method 上移到了方法内部, 对代码块进行同步

    优点:缩小了同步范围,提高了效率
    缺点:多线程下,还是会存在 并发问题

    当线程A进入 if (instance == null) 还没来得及往下执行时,线程B也进来了,此时就会产生多个实例

    /**
     * 单例 - 饱汉/懒汉模式<br>
     */
    public class Lazy {
        private Lazy() {
        }
    
        private static Lazy instance;
    
        public static Lazy getInstance() {
            if (instance == null) {
                synchronized (Lazy.class) {
                    instance = new Lazy();
                }
            }
            return instance;
        }
    
    }
    
    
    再升级

    双重检查模式

    双重检查模式

    优点:不仅线程安全,而且延迟加载,效果更高
    缺点:需要使用 volatile 关键字

    为什么需要二次检查?
    见前面 懒汉模式的 升级 版的说法

    为什么需要 volatile 关键字呢?
    避免指令重排,拿到未初始化的对象
    instance = new DoubleCheck() 这句并非原子操作

    涉及到 三步
    1.给 instance 分配内存空间
    2.调用 DoubleCheck的构造器来初始化 instance
    3.将 instance 对象指向分配的内存空间(执行完这步instance就不为null了)

    这里需要注意1-2-3的顺序,可能存在 JVM 的优化(指令重排)
    也就是 2,3的顺序不一定能保证

    如图:
    [图片上传失败...(image-a153fb-1582641478169)]

    /**
     * 单例- 双重检查模式<br>
     */
    public class DoubleCheck {
        private DoubleCheck() {
        }
    
        private static volatile DoubleCheck instance;
    
        public static DoubleCheck getInstance() {
            if (instance == null) {
                synchronized (DoubleCheck.class) {
                    if (instance == null) {
                        instance = new DoubleCheck();
                    }
                }
            }
            return instance;
        }
    }
    

    静态内部类模式

    优点:
    静态内部类(InnerClass)方式在 InnerStaticClass 被装载时,并不会被初始化

    /**
     * 单例-静态内部类模式<br>
     */
    public class InnerStaticClass {
    
        private static InnerStaticClass instance;
    
        private InnerStaticClass() {
        }
    
        public static InnerStaticClass getInstance() {
            return InnerClass.instance;
        }
    
        private static class InnerClass {
            private InnerClass() {
            }
    
            private static final InnerStaticClass instance = new InnerStaticClass();
        }
    }
    

    综上:静态内部类与双重检查模式,都是一样的,延迟加载,避免了线程不安全,并且效率高
    但有一个问题就是:无法防止 反序列化以及反射

    枚举类型

    相关文章

      网友评论

          本文标题:设计模式之单例的实现

          本文链接:https://www.haomeiwen.com/subject/dsccchtx.html