• 单例模式


    单例模式

    为什么:

    • 逻辑上,一个公司只能有一个老板
    • 效率上,尽量少实例化对象避免空间占用
    • 其他

    1 饿汉模式

    最基本的思路,就是将类的构造器私有化,那么就不能在外部调用 new 创建实例了。

    其次,通过调用静态方法获取实例。

    // 一般情况来说,这种方式就够用了!
    
    public class Boss {
        private Boss() {}
    
        private static Boss instance = new Boss();
    
        public static Boss getInstance() {
            return instance;
        }
    }

    2 懒汉模式以及演进

    饿汉模式的问题在于,即使没有用到 boss,它也会被实例化,有些浪费空间…

    而懒汉模式就是让 boss 只在用到的时候才去加载。

    其设计的思路及代码如下:

    public class Boss {
        // 1. 私有化构造器
        private Boss {}
    
        // 2. 定义实例的变量
        private static Boss instance;
    
        // 3. 通过静态方法创建或返回实例
        public static Boss getInstance () {
            if (instance == null) {
                instance = new Boss();  // 虽然构造器是私有的,但是可以在内部调用
            }
            return instance;
        }
    }

    这种方法在单线程下没有任何问题,但是在多线程环境中,却可能会实例化出多个对象。也就是说,它并不是线程安全的。为了解决这个问题,需要对 getInstance 加锁:

    public class Boss {
        // 1. 私有化构造器
        private Boss {}
    
        // 2. 定义实例的变量
        private static Boss instance;
    
        // 3. 通过静态方法创建或返回实例
        public synchronized static Boss getInstance () { // 通过锁,将对此方法的调用变成串行的。这就防止了错误
            if (instance == null) {
                instance = new Boss();  // 虽然构造器是私有的,但是可以在内部调用
            }
            return instance;
        }
    }

    上述加锁的方式,可以保证正确实例化对象。但是,因为在方法上加了锁,使得获取单例对象的效率过低。这时候,需要兼顾线程安全和效率,就出现了双重检查锁的概念:

    // 1. 将构造器私有化
    private Boss() {}
    
    // 2. 初始化一个静态变量
    private static volatile Boss instance = null;
    
    // 3. 构造一个静态方法,通过它初始化或返还对象
    public static Boss getInstance() {
        // 双重检查锁机制
        if (instance == null) {
            synchronized (Boss.class) {
                if (instance == null) {
                    instance = new Boss();
                }
            }
        }
        return instance;
    }

    其中:

    • synchronized 块尽量缩小了锁定的范围,提高效率
    • volatile 是为防止编译器指令重排而导致双重检查锁失效

    另外:

    • 指令重排本是为了优化代码执行效率而存在的,虽然在单线程中效果拔群,但是在多线程中却能带来麻烦。volatile 可以要求编译器不要做指令重排。

    3 静态内部类方式

    这是相对来说,非常优秀的一种实现。在很多地方,推荐使用这种方式。

    public class Boss {
        // 1. 将构造器私有化
        private Boss() { }
    
        // 2. 充分利用了静态内部类的特性,在里面初始化 Boss 实例
        //    - 只会被初始化一次
        //    - 只有当静态内部类内部的属性、方法等被调用的时候,静态内部类才会被加载
        static class Singleton {
            private final static Boss INSTANCE = new Boss();
        }
    
        // 3. 提供一个公共方法,获取实例化好之后的对象
        public static Boss getInstance() {
            return Singleton.INSTANCE;
        }
    }

    4 ENUM方式

    ENUM 应该是最简单,也是最好的一种实现单例模式的方式。

    它充分利用了 JVM 的特性,既保证了线程安全,又保证了延迟加载。

    enum Boss {
        INSTANCE;
    
        public void sayHello () {
            System.out.println("hello");
        }
    }
    
    public class Main {
        public static void main (String... args) {
            Boss theBoss = Boss.INSTANCE;  // 获取实例
            theBoss.sayHello();            // 调用方法
        }
    }
  • 相关阅读:
    js代码性能优化的几个方法
    BOM(浏览器对象模型)的一些内置对象总结
    js产生对象的3种基本方式(工厂模式,构造函数模式,原型模式)
    ELF文件格式
    Python 语言之 map/reduce
    LeetCode
    快速排序
    网络问题诊断
    Notepad++ 用法技巧
    Python图形开发之PIL
  • 原文地址:https://www.cnblogs.com/guapishuo/p/10102291.html
Copyright © 2020-2023  润新知