【3W2H设计模式】单例模式

简介: 【3W2H设计模式】单例模式

3W2H学习设计模式-单例模式

一、WHAT 什么是单例模式

单例模式(Singleton Pattern)是创建型模式一种,是设计模式中最简单的设计模式之一。它提供了一种创建对象的最佳方式。

这种模式是一个单一的类,该类负责创建自己的对象,同时确保只有单个对象被创建。这个类提供了一种访问其唯一的对象的方式,可以直接访问,不需要实例化该类的对象。

说明:

  • 单例类只能有一个实例。
  • 单例类必须自己创建自己的唯一实例。
  • 单例类必须给所有其他对象提供这一实例。

二、WHY 为什么用单例模式

  • 单例模式在内存中有且只有一个对象被实例化,节省了内存空间
  • 避免频繁的创建销毁对象,可以提高性能
  • 避免对共享资源的多重占用
  • 可以全局访问

三、WHEN 什么时候用单例模式

单例模式节省内存,占用空间少,性能高等特点。所以单例模式适用以下几种场景:

  • 需要频繁实例化然后销毁的对象
  • 创建对象耗时过多或者耗资源过多,但又经常用到的对象
  • 有状态的工具类对象
  • 频繁访问数据库或文件的对象
  • 以及其他要求只有一个对象的场景

四、HOW 如何实现单例模式

前面已说了单例模式优点,及其使用场合。那么在开发中如果使用单例模式。单例模式如何实现的哪?

  1. 单例模式类图

  1. 单例模式是实现
    1).创建一个EagerSingleton类
//饥饿式(静态变量)
public class EagerSingleton {
    // 1.私有构造函数
    public EagerSingleton() {
    }
    // 2.在类中创建类对象
    private static EagerSingleton instance = new EagerSingleton();
    // 3.提供一个公共的访问方式,让实现类获取该对象
    public static EagerSingleton getInstance() {
        return instance;
    }
}
  1. 2). 从EagerSingleton类获取唯一的对象
public class SingleInstanceApp {
    public static void main(String[] args) {
        EagerSingleton singleton1 = EagerSingleton.getInstance();
        EagerSingleton singleton2 = EagerSingleton.getInstance();
        // System.out.println(singleton1.hashCode());
        // System.out.println(singleton2.hashCode());
        if (singleton1.equals(singleton2)) {
            System.out.println("同一个单例模式");
        }
    }
}
  1. 3).运行结果
同一个单例模式
  1. 单例模式几种实现方式
    1). 懒汉模式 - 线程不安全
/**
 * 懒汉模式 - 线程不安全
 */
public class Singleton {
    private static Singleton instance;
    private Singleton() {
    }
    public static Singleton getInstance() {
        return instance == null ? new Singleton() : instance;
    }
}
  1. 注意:这种方式是最基础的实现方法,这种方法不支持多线程。
    2).懒汉模式 - 线程安全
/**
 * 懒汉式,线程安全 ,效率低,尽量少用
 *
 */
public class Singleton {
    private static Singleton instance;
    private Singleton() {
    }
    public static synchronized Singleton getInstance() {
        return instance == null ? new Singleton() : instance;
    }
}
  1. 注意 :第一次调用才初始化,避免内存浪费;但是加锁 synchronized 保证单例,加锁会影响效率,建议少用。
    3).饥饿模式 - 线程不安全
/**
 * 饥饿模式 - 没有加锁,执行效率会提高;类加载时就初始化,浪费内存;
 *
 */
public class Singleton {
    private static Singleton instance = new Singleton();
    private Singleton() {
    }
    public static Singleton getInstance() {
        return instance;
    }
}
  1. 注意:它基于 classloader 机制避免了多线程的同步问题,没有加锁,执行效率会提高,但是因为类加载就初始化,会产生内存垃圾。造成内存浪费
    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;
    }
}
  1. 注意:双检锁/双重校验锁 - 这种模式线程安全并且在多线程下保存高效。
    5).登记式/静态内部类
/**
 * 登记式/静态内部类 -
 * 能达到双检锁方式一样的功能且简单。对静态域使用延迟初始化,应使用这种方式而不是双检锁方式。这种方式只适用于静态域的情况,双检锁方式在实例需要延迟初始化时使用。
 *
 */
public class Singleton {
    private static class SingletonHolder {
        private static final Singleton INSTANCE = new Singleton();
    }
    private Singleton() {
    }
    public static final Singleton getInstance() {
        return SingletonHolder.INSTANCE;
    }
}
  1. 注意:内部静态类实现的单例模式-使用是延时初始化,并达到双重校验锁的效果。
    6).枚举型单例模式
/**
 * 可避免多线程同步问题;还可以防止通过反射和反序列化来重新创建新的对象
 */
public class Singleton {
    private Singleton() {
    }
    public static enum SingletonEnum {
        SINGLETON;
        private Singleton instance = null;
        private SingletonEnum() {
            instance = new Singleton();
        }
        public Singleton getInstance() {
            return instance;
        }
    }
}
  1. 注意:枚举是在JDK1.5以及以后版本中增加的一个“语法糖”,它主要用于维护一些实例对象固定的类 枚举单例模式可避免多线程同步问题;还可以防止通过反射和反序列化来重新创建新的对象

五、HOW TO CHANGE(单例模式在代码中如何使用)

1.JDK 中 java.lang.Runtime 类,每个运行中的 Java 应用的环境信息,单例

public class Runtime {
   private static Runtime currentRuntime = new Runtime();
   public static Runtime getRuntime() {
         return currentRuntime;
   }
   private Runtime() {}
   .......
}

2.Spring 的单例 bean 的实现。Spring 的单例是 IoC 容器级别的

public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements SingletonBeanRegistry {    
    /** Cache of singleton objects: bean name to bean instance. */
  private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
    /** Cache of singleton factories: bean name to ObjectFactory. */
  private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);
  /** Cache of early singleton objects: bean name to bean instance. */
  private final Map<String, Object> earlySingletonObjects = new HashMap<>(16);
  @Nullable
  protected Object getSingleton(String beanName, boolean allowEarlyReference) {
    Object singletonObject = this.singletonObjects.get(beanName);
    if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
      synchronized (this.singletonObjects) {
        singletonObject = this.earlySingletonObjects.get(beanName);
        if (singletonObject == null && allowEarlyReference) {
          ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
          if (singletonFactory != null) {
            singletonObject = singletonFactory.getObject();
            this.earlySingletonObjects.put(beanName, singletonObject);
            this.singletonFactories.remove(beanName);
          }
        }
      }
    }
    return singletonObject;
  }
}

3.MyBatis中ErrorContext

package org.apache.ibatis.executor;
/**
 * @author Clinton Begin
 */
public class ErrorContext {
  private static final String LINE_SEPARATOR = System.getProperty("line.separator","\n");
  private static final ThreadLocal<ErrorContext> LOCAL = new ThreadLocal<ErrorContext>();
  //暂存线程上下文
  private ErrorContext stored;
  private String sql;
  ... //各个阶段的值存储
  private Throwable cause;
  private ErrorContext() {
  }
  public static ErrorContext instance() {
    //单例模式,从当前线程获取线程存储变量(一个)
    ErrorContext context = LOCAL.get();
    if (context == null) {
      context = new ErrorContext();
      LOCAL.set(context);
    }
    return context;
  }
  public ErrorContext store() {
    stored = this;
    LOCAL.set(new ErrorContext());
    return LOCAL.get();
  }
  public ErrorContext recall() {
    if (stored != null) {
      LOCAL.set(stored);
      stored = null;
    }
    return LOCAL.get();
  }
  public ErrorContext sql(String sql) {
    //这种写法是流式编程
    this.sql = sql;
    return this;
  }
  public ErrorContext cause(Throwable cause) {
    this.cause = cause;
    return this;
  }
  public ErrorContext reset() {
    ...各个阶段记录初始化
    sql = null;
    LOCAL.remove();
    return this;
  }
  @Override
  public String toString() {
    StringBuilder description = new StringBuilder();
    ...各阶段日志输出
    // sql
    if (sql != null) {
      description.append(LINE_SEPARATOR);
      description.append("### SQL: ");
      description.append(sql.replace('\n', ' ').replace('\r', ' ').replace('\t', ' ').trim());
    }
    // cause
    if (cause != null) {
      description.append(LINE_SEPARATOR);
      description.append("### Cause: ");
      description.append(cause.toString());
    }
    return description.toString();
  }
}


目录
相关文章
|
5天前
|
设计模式 安全 Java
【设计模式系列笔记】单例模式
单例模式是一种创建型设计模式,它确保一个类只有一个实例,并提供一个全局访问点,以便全局范围内访问这个实例。单例模式的目标是限制一个类的实例化,确保在整个应用程序中只有一个实例存在,并提供对这个唯一实例的全局访问点。这对于控制对资源的访问、限制特定类的实例数量等场景非常有用。
123 5
|
5天前
|
设计模式 安全 测试技术
【C/C++ 设计模式 单例】单例模式的选择策略:何时使用,何时避免
【C/C++ 设计模式 单例】单例模式的选择策略:何时使用,何时避免
67 0
|
5天前
|
设计模式 缓存 安全
【设计模式】单例模式:确保类只有一个实例
【设计模式】单例模式:确保类只有一个实例
26 0
|
5天前
|
设计模式 PHP
php设计模式--单例模式(三)
php设计模式--单例模式(三)
14 0
|
5天前
|
设计模式 安全 Java
【JAVA】Java 中什么叫单例设计模式?请用 Java 写出线程安全的单例模式
【JAVA】Java 中什么叫单例设计模式?请用 Java 写出线程安全的单例模式
|
5天前
|
设计模式 安全 Java
设计模式之单例模式
设计模式之单例模式
|
4天前
|
设计模式 SQL 安全
Java一分钟之-设计模式:单例模式的实现
【5月更文挑战第16天】本文介绍了单例模式的四种实现方式:饿汉式(静态初始化)、懒汉式(双检锁)、静态内部类和枚举单例,以及相关问题和解决方法。关注线程安全、反射攻击、序列化、生命周期和测试性,选择合适的实现方式以确保代码质量。了解单例模式的优缺点,谨慎使用,提升设计效率。
19 3
|
5天前
|
设计模式
【设计模式】单例模式的三种实现方式
【设计模式】单例模式的三种实现方式
7 1
|
5天前
|
设计模式 安全 Java
【设计模式学习】单例模式和工厂模式
【设计模式学习】单例模式和工厂模式
|
5天前
|
设计模式 安全 Java