设计模式 一 单例模式(创建型)

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

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

1、使用单例模式的原因

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

2、单例模式概述

2.1 单例模式要点
  • 一个类只能有一个实例对象,这个类称为单例类
  • 这个类的实例必须是这个类自己创建的,而不是使用 new
  • 这个类必须向整个系统提供这个实例,提供全局访问的方法
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;
    }
}

静态代码块:

class EagerSingle{
    private static EagerSingle eagerSingle;
    private EagerSingle(){}
    static {
		eagerSingle = new EagerSingle();
	}
    public static EagerSingle getInstance(){
        return eagerSingle;
    }
}
3.2 懒汉式

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

class LazySingle {
    private static LazySingle lazySingle = null;
    private LazySingle(){}
    public static synchronized 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;
    }
}
3.5 单例模式在 JDK 中的源码实现

java.lang.Runtime 就是一个经典的单例模式(饿汉式)

在这里插入图片描述

java.lang.Runtime 源码:

public class Runtime {
    private static Runtime currentRuntime = new Runtime();

    public static Runtime getRuntime() {
        return currentRuntime;
    }

    private Runtime() {}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

一起来搬砖呀

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值