单例模式

143 阅读4分钟

Singleton.

别名:单件模式


定义

保证一个类只有一个实例,并提供一个访问该实例的全局节点


应用场景

1.如果程序中的某个类对于所有客户端只有一个可用的实例

单例模式禁止通过除特殊方法以外的任何方式来创建自身的对象。该方法可以创建一个新对象,若该对象已创建,则返回已有的对象

2.需要严格地控制全局变量

单例模式保证类只存在一个实例。除了单例类自己以外,无法通过任何方法替换缓存的实例【可以随时调整限制并设定生成单例实例的数量,只需修改获取实例方法即可】


实现方法

1.在类中添加一个私有静态成员变量用于保存 单例实例

2.声明一个公有静态构建方法用于获取单例实例

3.在静态方法中实现 “ 延迟初始化 ”。该方法会在首次被调用时创建一个新对象,并将其存储在静态成员变量中。此后该方法每次被调用时都返回实例

4.将类的构造函数设为私有。类的静态方法仍能调用构造函数函数,但是其他对象不能调用。

5.检查客户端代码,将对单例的构造函数的调用替换为对其静态构建方法的调用


优缺点

优点:

1.保证一个类只有一个实例

2.获得一个指向该实例的全局访问节点

3.仅在首次请求单例对象时对其进行初始化

缺点:

1.违反了 单一职责原则。

2.可能会掩盖不良设计。eg:程序各组件之间相互了解过多

3.在多线程环境下需要进行特殊处理,避免多个线程多次创建单例对象


结构

UML图

classDiagram
Client-->Singleton
Singleton-->Singleton
class Singleton{
	-instance:Singleton
	-Singleton()
	+getInstance() Singleton
}

参与者

单例 ( Singleton ) :声明一个 getInstance (获取实例)的静态方法 来返回其所属类的一个相同实例。

通用写法

饿汉式

-- 直接在内存中new 实例

静态变量

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

静态代码块

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

优点:

在类装载时完成实例,避免线程同步问题

缺点:

未达到Lazy Loading的效果

若未使用该实例,则造成内存浪费

懒汉式

-- 只有在get该实例时才new对象

线程不安全

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

优缺点:

拥有Lazy Loading 效果。但仅在单线程下使用

多线程时,易存在if判断未执行时,已有其他线程产生该实例。

线程安全

使用多线程,进行同步方法。

使用同步代码块没有线程同步的作用,因为当进入判断时,容易产生多个实例。

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

优缺点:

解决了线程不安全的问题

  但效率太低了。当每个线程都想获取该实例,进入getInstance方法都需要进行同步,而该方法仅需要执行一次便足够了,若后面需要获取该实例们直接return即可。

双重检查

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

优缺点:

双重检查是多线程开发中常使用的。进行两次判断,保证线程安全。

实例化代码只进行一次,效率提高

静态内部类

静态内部类的特点:

1.进行类加载时,不会对其装载,保证懒加载。

2.调用时,则会去装载该类[ 仅一次 ],保证线程安全。

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

枚举

public enum Singleton {
   INSTANCE;
   
   public void show(){
       System.out.println("Singleton =>" + hashCode());
  }
}

借助枚举实现,不仅避免多线程同步问题,还能防止反序列化

容器式

适用于需要大量创建单例对象的场景,便于管理

public class Singleton {
   private Singleton(){}
   
   private static Map<String,Object> ioc = new ConcurrentHashMap<>();
   
   private static Object getBean(String className){
       synchronized (ioc){
           if (!ioc.containsKey(className)){
               Object obj = null;
               try {
                   obj = Class.forName(className).newInstance();
                   ioc.put(className,obj);
              }catch (Exception e){
                   e.printStackTrace();
              }
               return obj;
          }else {
               return ioc.get(className);
          }
      }
  }
}