设计模式 - 单例模式

Posted 思想累积

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了设计模式 - 单例模式相关的知识,希望对你有一定的参考价值。

创建者模式提供创建对象的机制,提升已有代码的灵活性和可复用性

创建者模式包括工厂模式、抽象工厂、生成器、原形、单例这 5 类

1、使用单例模式的原因

在开发中,有时候为了节省系统资源,我们需要确保系统中的某个类只有唯一一个实例,这个实例创建后,我们无法再创建一个同类型的其他对象,所有操作都是基于这个唯一的实例进行的。为了确保对象的唯一性,我们可以通过单例模式来进行实现。

2、单例模式概述

2.1 单例模式要点

  • 一个类只能有一个实例,这个类称为单例类
  • 这个类的实例必须是这个类自己创建的
  • 这个类必须向整个系统提供这个实例,提供全局访问的方法

2.2 单例模式实现步骤

首先,我们定义一个类 SingleBean,包含了构造方法和初始化的方法,代码如下:

public class SingleBean 
    public SingleBean()
    public void init();

为了实现该类的唯一性,我们通过以下步骤对该类进行重构

  • 首先为了防止每次使用 new 来实例化 SingleBean 都会产生一个新的对象,我们禁止类使用 new 来创建对象,将构造函数的修饰符改为 private

    public class SingleBean 
        private SingleBean()
        public void init();
    
    
    
  • 外部已经无法使用 new 来创建对象了后,我们在需要在类的内部创建并保存一个唯一的实例,提供给外部进行访问。为了封装我们将成员变量修饰符设置为 private

    public class SingleBean 
        private static SingleBean singleBean = null;
        
        private SingleBean()
        public void init();
    
    
    
  • 外界需要使用的时候,我们提供一个公共的静态方法对该类进行实例化

    使用 public static 修饰,保证方法可以直接通过类名访问到,在方法中首先判断 singleBean 对象是否存在,如果不存在,则 new 一个新的 SingleBean 对象返回,否则直接返回已有的 singleBean 对象

    public static SingleBean getInstance()
        if (singleBean == null) 
            singleBean = new SingleBean();
        
        return singleBean;
    
    

3、饿汉式和懒汉式单例

3.1 饿汉式

在定义变量的时候就实例化单例类,在类加载的时候就已经创建了对象

类在加载时,静态变量 eagerSingle 会被初始化,创建一个唯一的实例

class EagerSingle
    private static final EagerSingle eagerSingle = new EagerSingle();
    private EagerSingle();
    public static EagerSingle getInstance()
        return eagerSingle;
    

3.2 懒汉式

懒汉式在第一次调用单例类提供的获取实例对象的方法时,进行实例化,类加载的时候不进行实例化。为了避免多个线程同时调用,我们可以使用关键字 synchronized 进行线程锁

class LazySingle 
    private static LazySingle lazySingle = null;
    private LazySingle();
    synchronized public static LazySingle getInstance()
        if(lazySingle == null) 
            lazySingle = new LazySingle();
        
        return lazySingle;
    

增加线程锁后,多线程访问每次都需要进行判断,我们还可以继续对代码进行优化

我们不用对整个方法加锁,可以对进行实例化的代码块加锁,进行如下改进:

class LazySingle 
    private static LazySingle lazySingle = null;
    private LazySingle();
    public static LazySingle getInstance()
        if(lazySingle == null) 
            synchronized(LazySingle.class)
             	lazySingle = new LazySingle();   
            
        
        return lazySingle;
    

如果对代码块进行加锁后,看似不会出现问题,但是如果多个线程同时调用 getInstance 方法,如果变量 lazySingle 对象为 null,都可以进入 if 条件内,线程依次执行代码块内容,导致多个对象的产生

所以我们还需要再进行一次判断,在加锁代码块内对变量再次进行判断。为了保证可见性,需要对变量增加 volatile 关键字,保证多个线程能够正确处理

class LazySingle 
    private volatile static LazySingle lazySingle = null;
    private LazySingle();
    public static LazySingle getInstance()
        if(lazySingle == null) 
            synchronized(LazySingle.class)
                if (lazySingle == null) 
                    lazySingle = new LazySingle();  
                
            
        
        return lazySingle;
    

3.3 懒汉式与饿汉式单例比较

  • 饿汉式在类加载时就进行了实例化,不用考虑多线程访问问题,确保实例唯一性。但饿汉式单例不管用不用得到都会进行实例化,始终占据内存
  • 懒汉式在第一次使用时创建,延迟进行加载,为了处理多线程问题我们必须要进行双重锁定进行控制,性能会有一定影响

3.4 其它实现方式

使用静态内部类进行实现

class SingleBean 
    private SingleBean();
    
    private static class SingleBeanInstance 
        private final static SingleBean instance = new SingleBean();
    
    
    public static SingleBean getInstance() 
        return SingleBeanInstance.instance;
    

使用枚举进行实现

public enum EnumSingleBean 
    SINGLE_BEAN_INSTANCE;
    
    private SingleBean singleBean = null;
    
    private EnumSingleBean()
        singleBean = new SingleBean();
    
    
    public SingleBean getInstance()
        return singleBean;
    

以上是关于设计模式 - 单例模式的主要内容,如果未能解决你的问题,请参考以下文章

常用代码片段

性能比较好的单例写法

你熟悉的设计模式都有哪些?写出单例模式的实现代码

单例模式以及静态代码块

设计模式之单例模式

设计模式之单例模式