• 设计模式之创建型模式:单例模式


    一、设计模式概述

    设计模式是基于设计模式原则的基础上,由众多软件开发人员对所遇到的软件工程设计问题所总结的经验,它是某类问题的解决方案,代表了某类问题的最佳实践,它的最终目的是为了提高软件的可维护性,可读性,通用性,可扩展性等

    设计模式最经典的书籍就是由“四人组GOF”所编写的【设计模式】,该书将设计模式总结为三种类型,共23种,我后续的设计模式就按照这23种设计模式逐一讲解。

    23种设计模式分别如下:

    1. 创建型模式(5种):单例模式,抽象工厂模式,原型模式,建造者模式,工厂模式。
    2. 结构型模式(7种):适配器模式,桥接模式,装饰模式,组合模式,外观模式,享元模式,代理模式。
    3. 行为型模式(11种):模板方法模式,命令模式,访问者模式,迭代器模式,观察者模式,中介者模式,备忘录模式,解释器模式,状态模式,策略模式,职责链模式。

    本文讲解创建型模式中的单例模式。

    二、单例模式概述

    单例设计模式就是保证在整个软件系统中,对某个类只能存在一个对象实例,该对象实例由类提供一个静态方法获取。

    单例模式保证了系统内存中该类只存在一个对象,节省了系统资源,对于一些需要频繁创建销毁的对象,使用单例模式可以提高系统性能

    基于单例模式的特点,其应用场景如下:

    • 对于一些重量级的对象(创建时耗时或者耗费资源过多)
    • 需要频繁进行创建和销毁的对象
    • 经常用到的工具类对象
    • 频繁访问数据库或文件的对象

    三、单例模式实现

    1. 饿汉式实现

    所谓饿汉式实现就是指,不论这个类对象是否会用到,在类加载的时候就进行实例化

    饿汉式单例模式,直接对静态变量进行实例化,代码实现如下:

    class Singleton {
    	
    	//1. 构造器私有化, 外部能new
    	private Singleton() {
    		
    	}
    	
    	//2.本类内部直接创建对象实例
    	private final static Singleton instance = new Singleton();
    	
    	//3. 提供一个公有的静态方法,返回实例对象
    	public static Singleton getInstance() {
    		return instance;
    	}
    	
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    饿汉式单例模式,将静态变量在静态代码块中进行实例化,代码实现如下:

    class Singleton {
    	
    	//1. 构造器私有化, 外部能new
    	private Singleton() {
    		
    	}
    	
    
    	//2.本类内部创建对象实例
    	private  static Singleton instance;
    	
    	static { // 在静态代码块中,创建单例对象
    		instance = new Singleton();
    	}
    	
    	//3. 提供一个公有的静态方法,返回实例对象
    	public static Singleton getInstance() {
    		return instance;
    	}
    	
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    2. 懒汉式实现

    所谓懒汉式实现是指只有用到某个类对象时才会创建,用不到就不会创建,可以减少系统资源浪费。具体实现方法包括多线性不安全和多线程安全两种类型,对应线程不安全的实现只适用于单线程使用。

    2.1 多线程不安全的实现

    • 代码实现:
    class Singleton {
    	private static Singleton instance;
    	
    	private Singleton() {}
    	
    	//提供一个静态的公有方法,当使用到该方法时,才去创建 instance
    	//即懒汉式
    	public static Singleton getInstance() {
    		if(instance == null) {
    			instance = new Singleton();
    		}
    		return instance;
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 为什么多线程不安全?
      因为在多线性环境下,有可能多个线程同时进入if代码块,这样就会导致多个线程都进行实例化操作,产生不一样的对象

    2.2 多线程安全的实现

    2.2.1 synchronized实现

    代码实现:

    class Singleton {
    	private static Singleton instance;
    	
    	private Singleton() {}
    	
    	//提供一个静态的公有方法,加入同步处理的代码,解决线程安全问题
    	//即懒汉式
    	public static synchronized Singleton getInstance() {
    		if(instance == null) {
    			instance = new Singleton();
    		}
    		return instance;
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 分析
      因为在获取实例化对象的方法上添加了同步关键字,就可以保证每次只有一个线程进入该方法,保证了多线程必然得到的是同一个实例化对象。但是这种实现方法的缺点是造成效率降低,因为每一个线程访问都要进行同步,但理论上只要一个线程完成实例化后,其它线程就不会再存在得到不同对象的问题了,因此可以不需要同步操作了。
      为了解决该实现方法造成的效率低的问题,提出双重检查实现方法,该方法既可以保证线程安全,又可以尽可能的少的使用同步代码块。
    2.2.2 双重检查实现
    • 代码实现
    class Singleton {
    	private static volatile Singleton instance;//必须使用volatile关键字,防止指令重排
    	
    	private Singleton() {}
    	
    	//提供一个静态的公有方法,加入双重检查代码,解决线程安全问题, 同时解决懒加载问题
    	//同时保证了效率, 推荐使用
    	
    	public static Singleton getInstance() {
    		if(instance == null) {//如果尚未创建对象,可能多个线程进入下面的代码块
    			synchronized (Singleton.class) {//保证一次只能有一个线程进入其代码块
    				if(instance == null) {//假设一个线程已经实例化了对象,如果再有线程进来,就会发现instance不为null,就不会再实例化对象
    					instance = new Singleton();
    				}
    			}
    			
    		}
    		return instance;
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    2.2.3 静态内部类实现
    • 代码实现
    class Singleton {
    	//构造器私有化
    	private Singleton() {}
    	
    	//写一个静态内部类,该类中有一个静态属性 Singleton
    	private static class SingletonInstance {
    		private static final Singleton INSTANCE = new Singleton(); 
    	}
    	
    	//提供一个静态的公有方法,直接返回SingletonInstance.INSTANCE
    	
    	public static Singleton getInstance() {
    		
    		return SingletonInstance.INSTANCE;
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 分析
      静态内部类在外面的类加载的时候不会初始化,只有使用到内部类时才会由jvm类加载器加载,而JVM可以保证线程的安全性,因此实现了线程安全,并且很明显实现了懒加载。

    3. 枚举实现

    • 代码实现
    enum Singleton {
    	INSTANCE; //属性
    }
    
    • 1
    • 2
    • 3
    • 分析
      枚举实现单例模式是官方推荐的一种方法,枚举实现非常简单,并且基于JVM保证了线程安全。

    四、单例模式在JDK源码中的应用示例

    JDK中Runtime类就是实现了饿汉式单例模式,部分源码如下:
    在这里插入图片描述

  • 相关阅读:
    JAVA在线电子病历编辑器源码 B/S架构
    初识图学习
    京东商品详情API接口(PC端和APP端),京东详情页,商品属性接口,商品信息查询
    土壤养分检测:缺少氮磷钾元素时,作物表现的症状各不相同
    A-level化学知识点(二):一般原则——General Properties
    python 远程代码第一次推送
    parsel的使用
    记录一个教学的交互式系统的开发 —— 环境搭建
    WebSocket快速入门及基本使用
    BL200EC如何与欧姆龙相连
  • 原文地址:https://blog.csdn.net/qq_34720818/article/details/126429304