设计模式-单例模式

216 阅读4分钟

文章结构:意义是什么,解决了什么,实例,举例,建议

意义:

保证,在一个类里面,只有一个实例,并且提供一个全局访问的点,且单例类只有一个实例,并且单例类必须创建自己的实例,也要给外界提供一个访问

解决:

主要时解决了,在JVM虚拟机对全局使用的类频繁的创建和销毁,提高你的代码在JVM虚拟机里面的性能。

优点

1:在内存中只有一个实例,减少内存开销,减少GC浪费的资源
2:避免对资源的多重占用

缺点

没有接口,不能集成,与单一职责冲突,一个类因该关注内部逻辑,而不关心外面怎样实例化

实例:

基础实现-1

创建一个单例对类,并且定义一个单例的对象,私有的静态的,不可改变的,并提供getInstance()方法私有化构造器,让其他的调用者不可以创建对象


public class SingleObject {
 
   //创建 SingleObject 的一个对象
   private static SingleObject instance = new SingleObject();
 
   //让构造函数为 private,这样该类就不会被实例化
   private SingleObject(){}
 
   //获取唯一可用的对象
   public static SingleObject getInstance(){
      return instance;
   }
 
   public void showMessage(){
      System.out.println("Hello World!");
   }
}


//调用获取方法
public class SingletonPatternDemo {
   public static void main(String[] args) {
 
      //不合法的构造函数
      //编译时错误:构造函数 SingleObject() 是不可见的
      //SingleObject object = new SingleObject();
 
      //获取唯一可用的对象
      SingleObject object = SingleObject.getInstance();
 
      //显示消息
      object.showMessage();
   }
}

单例-懒汉式-之线程不安全-1

改单例模式的创建方式,时属于懒加载的创建方式,只有当使用者调用的时候,才会去创建该对象,单该方式不属于线程安全模式,无法在多线程下运行,会有脏数据的情况出现。


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

   public static Singleton getInstance() {
      if (instance == null) {
         instance = new Singleton();
      }
      return instance;
   }
}

单例-懒汉式-之线程安全-2

该创建方式,和线程不安全的方式最大的区别就是在获取实例方法上加了重锁,这样就可以保障在多线程下,保证实例获取的安全性,但是也同样存在缺点-就是性能比较低下

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

单例-饿汉式-之线程安全-3

该创建方式,比较常用,但是会产生较多的垃圾,在jvm启动初期就会创建该对象,不过该创建方式没有加锁执行效率还是比较高;
为什么这样子就是线程安全的:基于classloader机制避免了线程不安全的问题(网页解析classloader 书籍解析:深入理解JAVA虚拟机第三版p/48对象的创建),不过因为此关系该加载方式也不是懒加载

public class Singleton {
   private static Singleton instance = new Singleton();
   private Singleton (){}
   public static Singleton getInstance() {
      return instance;
   }
}

单例-双检锁/双重校验(DLC,即double-checked locking )-4

该创建方式:该方式避免了在项目启动初期就创建该对象,并且当线程并发的情况下保证了,线程安全的特性,而且也不会因为加了锁而导致性能低下

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

单例-登记/静态内部类 -5

该创建方式:相对于饿汉式线程安全就好很多,饿汉式线程安全在类加载时该类内部的对象就被加载了,没有实现懒加载的模式,而相对于双锁方式,该类实现更加简单并且可以实现同样的功效,该类在被加载时并不会创建INSTANCE对象因为SingletonHolder方法没有被显式调用,所以并不会被初始化,只有getInstance()方法被调用时才会创建该类的对象,当然该方法是线程安全还是基于classloader加载方式

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

枚举

该创建方式,是最好的单例模式,绝对防止多次实例化,且支持自动序列化机制,反序列化为新的对象,绝对防止多次实例化。 (并不是懒加载)

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

建议:

现在jdk已经远大于5 那么单例模式早已经在各大项目中被广泛使用,建议使用单例模式,其他的按照需求,但明确不推荐1,2