3W2H学习设计模式-单例模式
一、WHAT 什么是单例模式
单例模式(Singleton Pattern)是创建型模式一种,是设计模式中最简单的设计模式之一。它提供了一种创建对象的最佳方式。
这种模式是一个单一的类,该类负责创建自己的对象,同时确保只有单个对象被创建。这个类提供了一种访问其唯一的对象的方式,可以直接访问,不需要实例化该类的对象。
说明:
- 单例类只能有一个实例。
- 单例类必须自己创建自己的唯一实例。
- 单例类必须给所有其他对象提供这一实例。
二、WHY 为什么用单例模式
- 单例模式在内存中有且只有一个对象被实例化,节省了内存空间
- 避免频繁的创建销毁对象,可以提高性能
- 避免对共享资源的多重占用
- 可以全局访问
三、WHEN 什么时候用单例模式
单例模式节省内存,占用空间少,性能高等特点。所以单例模式适用以下几种场景:
- 需要频繁实例化然后销毁的对象
- 创建对象耗时过多或者耗资源过多,但又经常用到的对象
- 有状态的工具类对象
- 频繁访问数据库或文件的对象
- 以及其他要求只有一个对象的场景
四、HOW 如何实现单例模式
前面已说了单例模式优点,及其使用场合。那么在开发中如果使用单例模式。单例模式如何实现的哪?
//饥饿式(静态变量) public class EagerSingleton { // 1.私有构造函数 public EagerSingleton() { } // 2.在类中创建类对象 private static EagerSingleton instance = new EagerSingleton(); // 3.提供一个公共的访问方式,让实现类获取该对象 public static EagerSingleton getInstance() { return instance; } }
- 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("同一个单例模式"); } } }
- 3).运行结果
同一个单例模式
/** * 懒汉模式 - 线程不安全 */ public class Singleton { private static Singleton instance; private Singleton() { } public static Singleton getInstance() { return instance == null ? new Singleton() : instance; } }
/** * 懒汉式,线程安全 ,效率低,尽量少用 * */ public class Singleton { private static Singleton instance; private Singleton() { } public static synchronized Singleton getInstance() { return instance == null ? new Singleton() : instance; } }
/** * 饥饿模式 - 没有加锁,执行效率会提高;类加载时就初始化,浪费内存; * */ public class Singleton { private static Singleton instance = new Singleton(); private Singleton() { } public static Singleton getInstance() { return instance; } }
- 注意:它基于 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; } }
/** * 登记式/静态内部类 - * 能达到双检锁方式一样的功能且简单。对静态域使用延迟初始化,应使用这种方式而不是双检锁方式。这种方式只适用于静态域的情况,双检锁方式在实例需要延迟初始化时使用。 * */ 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 class Singleton { private Singleton() { } public static enum SingletonEnum { SINGLETON; private Singleton instance = null; private SingletonEnum() { instance = new Singleton(); } public Singleton getInstance() { return instance; } } }
- 注意:枚举是在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; } }
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(); } }