设计模式第一章——单例模式
一、设计模式分类
在开始介绍单例模式之前,我们先了解一下设计模式的分类。宏观维度分为三⼤类:创建型、结构型、行为型。实例代码
创建型:顾名思义就是给我们提供⼀种对象创建的功能,让我们只关注具体的使⽤⽽不是对象的创建过程。它包含的设计模式有单例模式、建造者模式、⼯⼚模式、抽象⼯⼚模式及原型模式。
结构型:它是使⽤组合的⽅式将类结合起来,从⽽可以⽤它来实现新的功能。它包含的设计模式是适配器模式、代理模式、组合模式、装饰模式及⻔⾯模式。
行为型:关注的是对象的⾏为,它是把对象之间的关系进⾏梳理划分和归类。它包含的设计模式有模板⽅法模式、命令模式、策略模式和责任链模式。
二、单例模式
单例模式(Singleton Pattern)是 Java 中最简单的设计模式之⼀,此模式保证某个类在运⾏期间,只有⼀个实例对外提供服务,⽽这个类被称为单例类。
单例模式的特点:可以减少系统内存开⽀,减少系统性能开销,避免对资源的多重占⽤、同时操作。例如:io操作、数据库链接、网络请求。
单例模式的缺点:扩展很困难,容易引发内存泄露,测试困难,⼀定程度上违背了单⼀职责原则,进程被杀时可能有状态不⼀致问题。
2.1 单例模式分类

2.1.1 饿汉式(线程安全,调用效率高。 但是,不能延时加载)
系统运⾏起来装载类的时候就进⾏初始化实例的操作,依赖 JVM 在类装载时就完成唯⼀对象的实例化。适用于对象占用内存,要求初始化速度快。
饿汉式三种实现方式:都依赖 JVM 在类装载时就完成唯⼀对象的实例化,基于类加载的机制,它们天⽣就是线程安全的,所以都是可⾏的,第⼆种更易于理解也⽐较常⻅。
//方式一 public class Singleton { public static final Singleton INSTANCE = new Singleton(); //私有化构造 private Singleton (){} } //方式二 public class Singleton { private static final Singleton INSTANCE = new Singleton(); private Singleton (){} public static Singleton getInstance(){ return INSTANCE; } } //方式三 public class Singleton { private static Singleton instance = null; static { instance = new Singleton(); } private Singleton (){} public static Singleton getInstance(){ return instance; } }
2.1.2 懒汉式(线程安全,调用效率不高。 但是,可以延时加载。)
懒汉式要使用双重校验的方式实现单例比较常用的实现方式。在声明变量时使⽤了 volatile(JDK版本需要大于1.5) 关键字来保证其线程间的可⻅性;
在同步代码块中使⽤⼆次检查,以保证其不被重复实例化 同时在调⽤getInstance()⽅法时不进⾏同步锁,效率⾼。这种实现⽅式既保证了其⾼效性,也保证了其线程安全性。
public class DCLLazy { private static volatile DCLLazy singleton; private DCLLazy(){} public static DCLLazy getInstance(){ if(singleton == null){ //第一次判断,避免不必要的同步(提高效率) synchronized (DCLLazy.class){ //同步 if(singleton == null){ //第二次判断,保证线程安全 singleton = new DCLLazy(); } } } return singleton; } }
2.1.3 静态内部类(线程安全,调用效率高。 但是,可以延时加载)
根据 静态内部类 的特性,同时解决了按需加载、线程安全的问题,同时实现简洁。这种⽅式利⽤了 classloder 的机制来保证初始化 instance 时只会有⼀个。需要注意的是:虽然它的名字中有“静态”两字,但它是属于“懒汉模式”的!!这种⽅式的 Singleton 类被装载时,只要 SingletonHolder 类还没有被主动使⽤,instance 就不会被初始化。只有在显式调⽤ getInstance() ⽅法时,才会装载SingletonHolder 类,从⽽实例化对象。“静态内部类”⽅式基本上弥补了 DCL ⽅式在JDK 版本低于 1.5 时⾼并发环境失效的缺陷。此⽅式并不特别常⻅,然⽽它是所有懒加载的单例实现中适⽤范围最⼴、限制最⼩、最为推荐的⼀种。
public class StaticInnerSingleton { //私有构造方法 private StaticInnerSingleton(){ } //创建一个私有的静态内部类,并且再内部定义一个外部类的静态成员 private static class SingletonHolder{ private static final StaticInnerClassSingleton SINGLETON = new StaticInnerClassSingleton(); } //提供一个public的方法获取外部类的对象 public static final StaticInnerClassSingleton getInstance(){ return SingletonHolder.SINGLETON; } }
2.1.4 枚举(线程安全,调用效率高,不能延时加载。弥补序列化和反射漏洞)
特点: 满⾜单例模式所需的 创建单例、线程安全、实现简洁的需求
public enum EnumSingleton { INSTANCE; public EnumSingleton getInstance(){ return INSTANCE; } }
2.1.5 ThreadLocal(懒汉式的扩展,利于线程间的传递)
import java.util.HashMap; import java.util.Map; public class MapContext { private static final ThreadLocal<MapContext> local = new ThreadLocal<>(); private Map<String,Object> data = new HashMap<>(); public Map<String, Object> getData() { return getAppContext().data; } //批量存数据 public void setData(Map<String, Object> data){ getAppContext().data.putAll(data); } //存数据 public void set(String key, String value) { getAppContext().data.put(key,value); } //取数据 public void get(String key) { getAppContext().data.get(key); } //初始化的实现⽅法 private static MapContext init(){ MapContext context = new MapContext(); local.set(context); return context; } //做延迟初始化 public static MapContext getAppContext(){ MapContext context = local.get(); if (null == context) { context = init(); } return context; } //删除实例 public static void remove() { local.remove(); } }
从代码上看是一个懒汉式,利用ThreadLocal的特性存储唯一对象实例。ThreadLocal原理参考《史上最全ThreadLocal 详解(一)》。

浙公网安备 33010602011771号