【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();
  }
}


目录
相关文章
|
1月前
|
设计模式 安全 Java
Kotlin教程笔记(57) - 改良设计模式 - 单例模式
Kotlin教程笔记(57) - 改良设计模式 - 单例模式
27 2
|
14天前
|
设计模式 安全 Java
Kotlin教程笔记(57) - 改良设计模式 - 单例模式
Kotlin教程笔记(57) - 改良设计模式 - 单例模式
|
22天前
|
设计模式 存储 数据库连接
PHP中的设计模式:单例模式的深入理解与应用
【10月更文挑战第22天】 在软件开发中,设计模式是解决特定问题的通用解决方案。本文将通过通俗易懂的语言和实例,深入探讨PHP中单例模式的概念、实现方法及其在实际开发中的应用,帮助读者更好地理解和运用这一重要的设计模式。
15 1
|
1月前
|
设计模式 存储 数据库连接
PHP中的设计模式:单例模式的深入解析与实践
在PHP开发中,设计模式是提高代码可维护性、扩展性和复用性的关键技术之一。本文将通过探讨单例模式,一种最常用的设计模式,来揭示其在PHP中的应用及优势。单例模式确保一个类仅有一个实例,并提供一个全局访问点。通过实际案例,我们将展示如何在PHP项目中有效实现单例模式,以及如何利用这一模式优化资源配置和管理。无论是PHP初学者还是经验丰富的开发者,都能从本文中获得有价值的见解和技巧,进而提升自己的编程实践。
|
1月前
|
设计模式 安全 Java
C# 一分钟浅谈:设计模式之单例模式
【10月更文挑战第9天】单例模式是软件开发中最常用的设计模式之一,旨在确保一个类只有一个实例,并提供一个全局访问点。本文介绍了单例模式的基本概念、实现方式(包括饿汉式、懒汉式和使用 `Lazy&lt;T&gt;` 的方法)、常见问题(如多线程和序列化问题)及其解决方案,并通过代码示例详细说明了这些内容。希望本文能帮助你在实际开发中更好地应用单例模式,提高代码质量和可维护性。
30 1
|
1月前
|
设计模式 缓存 数据库连接
探索PHP中的设计模式:单例模式的实现与应用
在PHP开发中,设计模式是提高代码可复用性、可维护性和扩展性的重要工具。本文将深入探讨单例模式(Singleton Pattern)的基本概念、在PHP中的实现方式以及实际应用场景。单例模式确保一个类仅有一个实例,并提供全局访问点。通过具体代码示例和详细解释,我们将展示如何在PHP项目中有效利用单例模式来解决实际问题,提升开发效率和应用性能。
|
1月前
|
设计模式 存储 测试技术
PHP中的设计模式:单例模式的深入解析与实践
在PHP开发领域,设计模式是解决常见问题的最佳实践。本文将深入探讨单例模式,一种确保类只有一个实例的设计模式,并提供实际应用示例。我们将从单例模式的基本概念讲起,通过实际案例展示如何在PHP中实现单例模式,以及它在不同场景下的应用和优势。最后,我们会探讨单例模式的优缺点,帮助开发者在实际项目中做出明智的选择。
|
1月前
|
设计模式 SQL 安全
PHP中的设计模式:单例模式的深入探索与实践在PHP开发领域,设计模式是解决常见问题的高效方案集合。它们不是具体的代码,而是一种编码和设计经验的总结。单例模式作为设计模式中的一种,确保了一个类仅有一个实例,并提供一个全局访问点。本文将深入探讨单例模式的基本概念、实现方式及其在PHP中的应用。
单例模式在PHP中的应用广泛,尤其在处理数据库连接、日志记录等场景时,能显著提高资源利用率和执行效率。本文从单例模式的定义出发,详细解释了其在PHP中的不同实现方法,并探讨了使用单例模式的优势与注意事项。通过对示例代码的分析,读者将能够理解如何在PHP项目中有效应用单例模式。
|
1月前
|
设计模式 存储 数据库连接
PHP中的设计模式:单例模式的深入探讨与实践
在PHP开发领域,设计模式是提升代码可读性、可维护性和扩展性的重要工具。本文聚焦于单例模式——一种确保类只有一个实例,并提供全局访问点的设计模式。我们将从定义、实现方式、应用场景以及在PHP框架中的运用等方面进行详细探讨,旨在帮助PHP开发者更好地理解和运用单例模式。
|
28天前
|
设计模式 安全 Java
Kotlin教程笔记(57) - 改良设计模式 - 单例模式
Kotlin教程笔记(57) - 改良设计模式 - 单例模式
25 0