zoukankan      html  css  js  c++  java
  • 【单例模式】java实现

    概述:确保一个类只有一个实例,而且自行实例化并向整个系统提供这个实例。

    关键点:

    1. 构造函数不对外开放,一般为private。
    2. 通过一个静态方法或者枚举返回单例类对象。
    3. 确保单例类的对象有且只有一个,尤其在多线程情况下。
    4. 确保单例类对象在反序列化时不会重新构建对象

    (1)饿汉模式

    饿汉式单例模式(在类加载时就完成了初始化,所以类加载较慢,但获取对象的速度快)

    public class EagerSingle {
        //饿汉模式单例
        //在类加载时就完成了初始化,所以类加载较慢,但获取对象的速度快
        private static EagerSingle single = new EagerSingle();//静态私有成员,已初始化
    
        private EagerSingle() {
            //私有构造函数
        }
    
        public static EagerSingle getInstance() {//静态,不用同步(类加载时已初始化,不会有多线程的问题)
            return single;
        }
    }

    (2)懒汉模式

    懒汉模式声明一个静态对象,并且在用户第一次调用getInstance时进行初始化。

    public class LazySingleton {
        //懒汉模式单例
        //比较懒,在类加载时不创建实例,因此类加载熟读快,但运行时获取对象速度慢
        private static LazySingleton instance;//静态私有成员,没有初始化
    
        private LazySingleton() {
            //私有构造函数
        }
    
        public static synchronized LazySingleton getInstance() {//静态、同步、公开访问
            if (instance == null) {
                instance = new LazySingleton();
            }
            return instance;
        }
    }

    synchronized关键字保证了同步,在多线程情况下单例的唯一性。

    存在问题:即使instance已经存在,每次调用getInstance依然会进行同步,这样就会消耗不必要的资源。

    总结:懒汉模式的优点是只有在使用时才会实例化单例对象,在一定程度上节约了资源;缺点是第一次加载时需要进行实例化,反应稍慢;最大问题是每次调用都会进行同步吗,造成不必要的同步开销。这种模式一般不建议使用。

    (3)Double Check Lock(DCL)双重校验锁

    DCL方式实现单例的优点是既能在需要时才初始化单例,又能保证线程安全,且单例对象初始化后调用getInstance不进行同步锁。

    public class DCLSingleton {
        //Double Check Lock单例模式
        //懒汉模式的改进
        //但仍然存在隐患
        private static DCLSingleton instance = null;
    
        private DCLSingleton() {
        }
    
        public static DCLSingleton getInstance() {
            if (instance == null) {//第一层判断主要是为了避免不必要的同步
                synchronized (DCLSingleton.class) {
                    if (instance == null) {//第二层判空是为了在null情况下创建实例
                        instance = new DCLSingleton();
                    }
                }
            }
            return instance;
        }
    }

    亮点在getInstance方法上,有两次判空。第一层判断主要是为了避免不必要的同步,第二层判空是为了在null情况下创建实例。

    • 执行下面这行代码
    single = new Singleton();

    实际上并不是一个原子操作,这句代码实际做了3件事

    1. 给Singleton的实例分配内存;
    2. 调用Singleton()的构造函数,初始化成员字段
    3. 将instance对象指向分配的内存空间(此时instance已经不是null了)

    问题:但由于java编译器允许处理器乱序执行,上述顺序2、3是不能保证的,可能是1-2-3也可能是1-3-2;如果是后者,3执行了已经非空,再走2会出现问题,这就是DCL失效

    解决: volatile关键字

    //    private static DCLSingleton instance = null;
        private volatile static DCLSingleton instance = null;

    只需要加上volatile关键字,如上述代码操作就可以保证instance对象每次都是从主内存中读取的,就可以采用DCL来完成单例模式了。当然,volatile或多或少会影响到性能,但考虑到程序的正确性,牺牲点性能还是值得的。

    总结:

    • 优点:资源利用率高,第一次执行getInstance时单例对象才会被实例化,效率高。
    • 缺点:第一次加载时反应稍慢;由于java内存模型的原因偶尔会失败,在高并发环境下也有一定的缺陷,虽然概率很小。
    • DCL模式是使用最多的单例实现方式

    (4)静态内部类单例模式

    public class InnerSingleton {
        private InnerSingleton() {
        }
    
        public static InnerSingleton getInstance() {
            return InnerSingletonHolder.instance;
        }
    
        /**
         * 静态内部类
         */
        private static class InnerSingletonHolder {
            private static final InnerSingleton instance = new InnerSingleton();
        }
    }

    总结:第一次加载InnerSingleton类时并不会初始化instance,只有在第一次调用InnerSingleton的getInstance方法时才会导致instance被初始化。因此,第一次调用getInstance方法会导致虚拟机加载InnerSingleton类,这种方法不仅能保证线程安全,也能够保证单例对象的唯一性,同时也延迟了单例的实例化,所以这也是一种推荐的单例模式实现方法

    (5)枚举单例

    public enum EnumSingleton {
        INSTANCE;
    
        public void doSomething() {
            //do sth ...
        }
    }

    震惊?没错!就是枚举!

    • 写法简单;枚举在java种与普通类是一样的,不仅能够有字段,还能够有自己的方法。最重要的是默认枚举实例的创建时线程安全的,并且在任何情况下都是一个单例。
    • 为什么这么说呢?  在上述的集中单例模式实现种,在一个情况下他们都会出现重新创建对象的情况,那就是反序列化

    补充: 通过序列化可以将一个单例的实例对象写到磁盘,然后再读回来,从而有效的获取一个实例。即使构造函数是私有的,反序列化时依然可以通过特殊的途径去创建类的一个新的实例,相当于调用该类的构造函数。

  • 相关阅读:
    流水线操作verilog
    16x16移位相加乘法器verilog实现
    Nios II对flash进行读写(DE2)
    initial使用的要点
    边沿检测电路设计verilog
    DDoS攻防战 (四):CC攻击防御系统部署
    DDoS攻防战(三):ip黑白名单防火墙frdev的原理与实现
    DDoS攻防战 (二) :CC攻击工具实现与防御理论
    DDoS攻防战 (一) : 概述
    IP流量重放与pcap文件格式解析
  • 原文地址:https://www.cnblogs.com/ivoo/p/10725893.html
Copyright © 2011-2022 走看看