• 设计模式单例模式


    单例模式

      定义:确保一个类最多只有一个实例,并提供一个全局访问点

      单例模式可以分为两种:预加载和懒加载

    1.1 预加载
      顾名思义,就是预先加载。再进一步解释就是还没有使用该单例对象,但是,该单例对象就已经被加载到内存了。

      public class PreloadSingleton {
        public static PreloadSingleton instance = new PreloadSingleton();
        /其他的类无法实例化单例类的对象
        private PreloadSingleton() {
        };
        public static PreloadSingleton getInstance() {
          return instance;
        }
      }
      很明显,没有使用该单例对象,该对象就被加载到了内存,会造成内存的浪费。

    1.2 懒加载
      为了避免内存的浪费,我们可以采用懒加载,即用到该单例对象的时候再创建。

      public class Singleton {
        private static Singleton instance=null;
        private Singleton(){
        };

        public static Singleton getInstance()
        {
          if(instance==null)
          {
            instance=new Singleton();
          }
          return instance;
        }
      }  
    1.3 单例模式和线程安全
      (1)预加载只有一条语句return instance,这显然可以保证线程安全。但是,我们知道预加载会造成内存的浪费。

      (2)懒加载不浪费内存,但是无法保证线程的安全。首先,if判断以及其内存执行代码是非原子性的。其次,new Singleton()无法保证执行的顺序性。

        不满足原子性或者顺序性,线程肯定是不安全的,这是基本的常识,不再赘述。我主要讲一下为什么new Singleton()无法保证顺序性。我们知道创建一个对象分三步:

        memory=allocate();//1:初始化内存空间

        ctorInstance(memory);//2:初始化对象

        instance=memory();//3:设置instance指向刚分配的内存地址


        jvm为了提高程序执行性能,会对没有依赖关系的代码进行重排序,上面2和3行代码可能被重新排序。我们用两个线程来说明线程是不安全的。线程A和线程B都创建对象。其中,A2和A3的重排序,将导致线程B在B1处判断出instance不为空,线程B接下来将访问instance引用的对象。此时,线程B将会访问到一个还未初始化的对象(线程不安全)。

     

    1.4 保证懒加载的线程安全
      我们首先想到的就是使用synchronized关键字。synchronized加载getInstace()函数上确实保证了线程的安全。但是,如果要经常的调用getInstance()方法,不管有没有初始化实例,都会唤醒和阻塞线程。为了避免线程的上下文切换消耗大量时间,如果对象已经实例化了,我们没有必要再使用synchronized加锁,直接返回对象。

      public class Singleton {
        private static Singleton instance = null;
        private Singleton() {
        };
        public static synchronized Singleton getInstance() {
          if (instance == null) {
            instance = new Singleton();
          }
          return instance;
        }
      }
      我们把sychronized加在if(instance==null)判断语句里面,保证instance未实例化的时候才加锁

      public class Singleton {
        private static Singleton instance = null;
        private Singleton() {
        };
        public static synchronized Singleton getInstance() {
          if (instance == null) {
            synchronized (Singleton.class) {
              if (instance == null) {
                instance = new Singleton();
              }
            }
          }
          return instance;
        }
      }
      我们经过1.3的讨论知道new一个对象的代码是无法保证顺序性的,因此,我们需要使用另一个关键字volatile保证对象实例化过程的顺序性。

      public class Singleton {
        private static volatile Singleton instance = null;
        private Singleton() {
        };
        public static synchronized Singleton getInstance() {
          if (instance == null) {
            synchronized (instance) {
              if (instance == null) {
                instance = new Singleton();
              }
            }
          }
          return instance;
        }
      }
      到此,我们就保证了懒加载的线程安全。

  • 相关阅读:
    数值分析实验之常微分方程的数值解法(MATLAB实现)
    Hadoop 伪分布模式安装
    Word 如何标齐选项
    在windows上安装elasticsearch7.6
    《转载》python/人工智能/Tensorflow/自然语言处理/计算机视觉/机器学习学习资源分享
    使用fastai训练的一个性别识别模型
    小程序录音、上传、转格式、语音识别爬坑记(微信版本:6.6.7及以上)
    centos 7.0 下安装FFmpeg软件 过程
    用python实现matlib的 生成高斯模糊核
    opencv如何在jupyter notebook中显示图片
  • 原文地址:https://www.cnblogs.com/zwbsoft/p/16143365.html
Copyright © 2020-2023  润新知