Java设计模式之单例模式

246 阅读4分钟

    上次讲到工厂模式,可以说单例模式是除工厂模式之外,我们还算熟悉的另一种设计模式了

(对于我这种渣渣而言)。

     单例模式其实很简单,就是某一个对象或者类,在程序运行期间,全局只初始化一次,其他

方法都使用同一个对象,这样对于某些特定的场景是有需求的,例如读取配置文件,没必要每

次读取都要重新new一个对象。

     那么我们要实现一个单例,有很多方式,包括饿汉式、懒汉式、双重检测锁、静态内部类、

和枚举来实现,

      1,下面是静态内部类实现:

//单例类
public class Singleton{
       //构造函数
       private Singleton(){}
       //这里使用内部静态类来实现单例
       private static Singleton createSingleton(){
              private static Singleton singleton = new Singleton();
       } 
       //对外提供获取单例的接口方法
       public Singleton getSingleton(){
              return createSingleton.singleton;
       }
}

      其实最推荐静态内部类实现,因为此方法运行期间不会主动加载,什么时候调用,什么时

候初始化实例,而且静态内部类由于其特性,天然的线程安全,而且内存占用第,效率高,那

么他就如此完美嘛?当然有缺点,就是静态类无法传参数。

      2,下面是饿汉式实现单例

public class Singleton{
       //构造
       private Singleton(){}
       //利用类加载器 静态初始化单例
       private static Singleton singleton = new Singleton();   
       //給外部提供获取方法
       public static getInstance(){
              return singleton;
       }
}

      为什么叫饿汉,因为它在类加载的过程中就已经初始化了,已经是实例化过了,所以无论

你要不要,它都在那里,所以不能实现延迟加载。所以当你getInstance的时候没有延迟,随意

加载,调用效率极高。

       3,下面是懒汉式实现单例

public class Singleton{
       //构造
       private Singleton(){}
       //定义
       private static Singleton singleton = null; 
       //給外部提供获取方法
       public static synchronized getInstance(){
              //如果单例为空 则初始化
              if (singleton==null){
                  singleton = new Singleton();
              }
              return singleton;
       }
}

         懒汉式顾名思义就是你什么时候用,我什么时候实例化,这样以来内存占用就比饿汉要

少一些,那么自然就可以实现延迟加载,但是调用效率并不高。对于懒汉式的一定要在

getInstance方法前加上Synchronized字段加锁,保证线程安全,不然就不再是单例了。

         4,利用枚举来实现单例,这个我很少使用

public enum Singleton{
      INSTANCE;
}

          只需要记住枚举是天然的线程安全,在任何情况下它都是一个单例,所以我们一般配置

一些返回状态码时候,都会使用枚举类型。

          5,双重锁实现单例,这个面试一般问的多些,但是平常并不使用,因为他偶尔会出问题。

public class Singleton {
    private volatile static Singleton singleton = null;

    private Singleton() {
    }

    public static Singleton getSingleton() {
       //先判断对象是否已经实例化
        if (singleton == null) {
            //对整个类加锁
            synchronized (Singleton.class) {
                if (singleton == null) {
                    singleton = new Singleton();
                }
            }
        }
        return singleton;
    }
}

          此方法先通过判断对象是否已经实例化,如果没有则加锁,再进行判断是否实例化。

因为有可能两个线程同时已经判断singleton为null时候,如果不加锁,有可能会被实例化两

次。所以先对整个类加锁,保证只有一个线程进行使用,再进行判断singleton,最后实例化。

          在这里又一个修饰符是volatile, 那么它主禁用jvm的乱序重排功能。因为在singleton实

例化的过程中并非一个原子操作,而是由三部分组成:

          1,首先为singleton分配内存空间

          2,之后要进行对于singleton的初始化 构造方法

          3,之后在把singleton指向分配的内存空间地址

         由于jvm具有乱序执行,很有可能分配玩内存空间之后,直接指向内存地址了,在单线程

环境下不会出现问题。但是到多线程环境下,thread1刚刚1-3还没有执行2,thread2就来了,

此时调用getSingleton,发现singleton不为null,但是singleton并未初始化。


效率排序:

饿汉>=静态内部类>=枚举>双重锁>>懒汉


还需要多多学习,java真是博大精深,自己还是渣渣一枚!