设计模式之单例模式解析

单例模式
1)动机

对于软件系统的某些类,无须创建多个实例,如 Windows 系统的任务管理器,重复对象会浪费系统资源。

2)概述
1.定义

确保某个类只有一个实例,而且自行实例化,并向整个系统提供这个实例,提供全局访问的方法。

在这里插入图片描述

2.角色

Singleton(单例)

在单例类的内部只生成一个实例,并提供一个静态的 getInstance() 工厂方法,让客户可以访问它的唯一实例;

为防止在外部对其实例化,将其构造函数设为私有;

在单例类内部定义了一个 Singleton 类型的静态对象,作为外部共享的唯一实例。

3)案例-简单单例模式

实现 Windows 任务管理器,类名为TaskManager,包含构造函数TaskManager(),显示进程的方法displayProcesses(),显示服务的方法displayServices();

class TaskManager
{
		 // 私有成员变量
     private static TaskManager tm = null;
     
     // 私有构造方法
     private TaskManager() {……} //初始化窗口
     
     // 成员方法
     public void  displayProcesses() {……} //显示进程
     public void  displayServices() {……} //显示服务
     
     // 工厂方法
     public static TaskManager getInstance()
     {
        if (tm == null)
        {
            tm = new TaskManager();
        }
        return tm;
    }
   ……
}
4)案例-饿汉式单例和懒汉式单例
1.饿汉式单例

定义静态变量时实例化单例类,当类被加载时,静态变量 instance 被初始化,此时类的私有构造函数会被调用,单例类的唯一实例将被创建。

在这里插入图片描述

class EagerSingleton { 
    private static final EagerSingleton instance = new EagerSingleton(); 
    private EagerSingleton() { } 
 
    public static EagerSingleton getInstance() {
        return instance; 
    }   
}
2.懒汉式单例

第一次调用 getInstance() 时实例化,在类加载时并不自行实例化,为避免多个线程同时调用 getInstance(),使用 synchronized。

在这里插入图片描述

class LazySingleton { 
    private static LazySingleton instance = null; 
    private LazySingleton() { } 
 
    synchronized public static LazySingleton getInstance() { 
        if (instance == null) {
            instance = new LazySingleton(); 
        }
        return instance; 
    }
}

问题

在 getInstance() 增加关键字 synchronized,以处理多个线程同时访问的问题,但是每次调用 getInstance( ) 时都进行线程锁定判断,在多线程高并发环境中,会导致系统性能大大降低。

方案

减小锁范围+双重检查锁定(Double-Check Locking);

class LazySingleton { 
		// 原子性 volatile
    private volatile static LazySingleton instance = null; 
 
    private LazySingleton() { } 
 
    public static LazySingleton getInstance() { 
        //第一重判断
        if (instance == null) {
            //锁定代码块
            synchronized (LazySingleton.class) {
                //第二重判断
                if (instance == null) {
                    instance = new LazySingleton(); //创建单例实例
                }
            }
        }
        return instance; 
    }
}
3.饿汉式单例类与懒汉式单例类对比
分类 类加载时机 资源利用 复杂度
饿汉式单例类 类加载时
懒汉式单例类 第一次使用时
5)案例-Initialization Demand Holder (IoDH) 实现单例类

在单例类中增加一个静态(static)内部类,在该内部类中创建单例对象,再将该单例对象通过 getInstance() 返回给外部使用。

class Singleton {
	private Singleton() {
	}
	
	private static class HolderClass {
       private final static Singleton instance = new Singleton();
	}
	
	public static Singleton getInstance() {
	    return HolderClass.instance;
	}
}

使用 IoDH,可以实现延迟加载,保证线程安全,不影响系统性能。

6)总结
1.优点

单例模式可以严格控制怎样以及何时访问。

在内存中只存在一个对象,可以节约系统资源,对于需要频繁创建和销毁的对象,单例模式可以提高系统性能。

允许可变数目的实例,使用与单例控制相似的方法来获得指定个数的对象实例。

2.缺点

因为单例模式中没有抽象层,所以单例类的扩展很困难;

单例类的职责过重;

如果实例化的共享对象长时间不被利用,会自动销毁并回收资源,下次利用时又将重新实例化,会导致共享的单例对象状态丢失。

3.适用场景

系统只需要一个实例对象,或者因资源消耗太大而只允许创建一个对象。

上一篇:STM32学习笔记(4)- OLED


下一篇:基于Google云原生工程师的kubernetes最佳实践(二)