• 单例模式


    单例模式:即是对于某对象的类只能允许一个实例(该对象)存在,这个对象即是单例。

    有时对于整个系统只需要一个全局对象,这样有利于协调系统的整体行为,例如,某服务器的配置信息存放在一个文件中,这些配置数据由一个单例对象统一读取,然后服务进程中的其他对象再通过单例对象来获取这些配置信息。这样就简化了系统的配置管理。

    单例对象之所以能作为一个全局对象存在于系统中,就是利用了jvm垃圾回收机制不会回收单例对象。(只要有引用指向该对象,这就是一个或者的对象,??网上还有一种说法:static 属性不会被回收。不管怎样只要允许单例的存在就不影响我们使用它)

    单例模式要求类能够有返回对象一个引用(永远是同一个)和一个获得该实例的方法(必须是静态方法,通常使用getInstance这个名称)。

    实现步骤:

      1.该类的构造方法私有化。这样其他地方无法通过该类的构造器来实例化一个对象,故而只能通过该类提供的静态方法来获得其对象;

      2.该类提供一个静态方法,当调用该方法时,如果类持有的引用不为空,就将其返回,否则,创建一个实例并保留其引用,然后返回。

    注意:

      单例模式在多线程的应用场合下必须小心使用。要避免两个以上的线程同时操作以免违反单例的遵旨。

    一、饿汉式

      优点:实现简单,装载时就完成实例化,避免了线程同步问题。

      缺点:没有Lazy—loading效果,若是系统用不到则会有资源浪费。

    /**
    静态常量
    */
    public class Singleton {
        private final static Singleton INSTANCE = new Singleton();
        private Singleton(){}
        public static Singleton getInstance(){
            return INSTANCE;
        }
    }
    /**
    静态代码块
    */
    public class Singleton{
        private static Singleton instance;
        static{
            instance = new Singleton();
        }
        private Singleton(){}
        public static Singleton getInstance(){
            return instance;
        }
    }

    二、懒汉式

    这种写法起到了Lazy Loading的效果,但是只能在单线程下使用。如果在多线程下, 一个线程进入了if (singleton == null)判断语句块,还未来得及往下执行,另一个线程也通过了这个判断语句,这时便会产生多个实例。 所以在多线程环境下不可使用这种方式。

    public class Singleton{
        private static Singleton singleton;
        private Singleton(){}
        public static Singleton getInstance(){
            if(singleton == null){
                singleton = new Singleton();
            }
            return singleton;
        }
    }

    再看这种写法:

    public class Singleton{
        private static Singleton singleton;
        private Singleton(){}
        public static synchronized Singleton getInstance(){
            if(singleton == null){
                singleton = new Singleton();
            }
            return singleton;
        }
    }

    这里用了关键字 synchronized 做了个线程同步,缺点: 效率太低,每个线程在想获得类的实例时候,执行getInstance()方法都要进行同步。 若是引用有对象直接return。

    public class Singleton {
        private static Singleton singleton;
        private Singleton() {}
        public static Singleton getInstance() {
            if (singleton == null) {
                synchronized (Singleton.class) {
                    singleton = new Singleton();
                }
            }
            return singleton;
        }
    }

    这里为了提高效率将线程锁直接加在了实例化代码,但是这种同步并不能起到线程同步的作用。跟第3种实现方式遇到的情形一致,假如一个线程进入了if (singleton == null)判断语句块,还未来得及往下执行, 另一个线程也通过了这个判断语句,这时便会产生多个实例。

    public class Singleton {
        private static volatile Singleton singleton;
        private Singleton() {}
        public static Singleton getInstance() {
            if (singleton == null) {
                synchronized (Singleton.class) {
                    if (singleton == null) {
                        singleton = new Singleton();
                    }
                }
            }
            return singleton;
        }
    }

    Double-Check概念对于多线程开发者来说不会陌生,如代码中所示,我们进行了两次if (singleton == null)检查, 这样就可以保证线程安全了。这样,实例化代码只用执行一次,后面再次访问时,判断if (singleton == null),直接return实例化对象。这里可以这样说 外层 if 语句用来提高效率,里层 if 语句用来确保单例。

    说完了饿汉懒汉,还有:

    静态内部类

    public class Singleton{
        private Singleton(){}
        private static class SingletonInstance{
            private static final Singleton INSTANCE = new Singleton(); 
        }
        public static Singleton getInstance(){
            return SingletonInstance.INSTANCE;
        }
    }

    这种方式跟饿汉式类似,但又有不同:两者都利用类加载机制来确保实例化时只有一个线程,不同的地方在饿汉式方式是只要Singleton类被装载就会实例化,没有Lazy-Loading的作用,而静态内部类方式在Singleton类被装载时并不会立即实例化,而是在需要实例化时,调用getInstance方法,才会装载SingletonInstance类,从而完成Singleton的实例化。
    类的静态属性只会在第一次加载类的时候初始化,所以在这里,JVM帮助我们保证了线程的安全性,在类进行初始化时,别的线程是无法进入的。
    优点:避免了线程不安全,延迟加载,效率高。

    枚举

    public enum Singleton{
        INSTANCE;
        public void whateverMethod() {
         
        }
    }

    不仅能避免多线程同步问题,而且还能防止反序列化重新创建新的对象。不过实际项目中用的很少。

  • 相关阅读:
    IT项目中使用 json格式数据 保存项目配置信息, 在配置文件再读取json文件的内容进行赋值
    python 使用eval() 可以将json格式的数据,转换为原始数据
    python 取出字典的键或者值/如何删除一个字典的键值对/如何遍历字典
    python2 中 unicode 和 str 之间的转换及与python3 str 的区别
    python 将列表嵌套字典的unicode字符串转换为str格式的字符串的方法
    Django 项目拆分配置文件settings.py
    苹果笔记本调整 pycharm 字体大小的地方
    django rest framework 向数据库中插入数据时处理外键的方法
    java实现文件的压缩和解压
    java 跨数据库导入大数据
  • 原文地址:https://www.cnblogs.com/lightandtruth/p/8446979.html
Copyright © 2020-2023  润新知