设计模式-单例模式

文章目录

  • 一、概述
  • 二、饿汉式
  • 三、懒汉式
    • 1. 非线程安全
    • 2. 线程安全


一、概述

当一个全局使用的类频繁地创建与销毁,为了控制实例的数量,节省系统资源,我们可以保证该类只有一个实例,并提供一个访问它的全局访问点。

单例模式是 Java 中最简单的设计模式之一,这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。单例模式确保某个类只有一个实例,而且自行实例化并向整个系统提供这个实例。

特点:

  • 单例类只有一个实例类
  • 单例类必须自己创建自己的唯一实例
  • 单例类必须给所有其它对象提供这一实例

核心:构造函数私有

单例模式又可分为饿汉式懒汉式


二、饿汉式

饿汉式单例设计模式就是使用类的时候已经将对象创建完毕,不管以后会不会用到该实例对象,先创建了再说。

代码如下(示例):

public class Singleton {
    
    // 在该类内部产生一个唯一的实例化对象,并且将其封装为 private static 类型的成员变量
    private static final Singleton instance = new Singleton();
    
    // 将构造方法私有化,使其不能在类的外部通过 new 关键字实例化该类对象
    private Singleton() {}
    
    // 定义一个静态方法返回这个唯一对象
    public static Singleton getInstance() {
        return instance;
    }
    
}

因为饿汉式在类创建的时候就已经创建好一个静态的对象供系统使用,以后不在改变,所以是线程安全的。


三、懒汉式

懒汉式单例设计模式就是调用 getInstance() 方法实例才被创建,先不急着实例化出对象,等要用的时候才实例出对象。

1. 非线程安全

public class Singleton {

    // 在该类内部产生一个唯一的实例化对象,并且将其封装为 private static 类型的成员变量
    private static Singleton instance = null;

    // 将构造方法私有化,使其不能在类的外部通过 new 关键字实例化该类对象
    private Singleton() {}

    // 定义一个静态方法返回这个唯一对象
    public static Singleton getInstance() {
        // 判断实例是否为 null
        if (instance == null) {
            // 创建一个实例
            instance = new Singleton();
        }
        // 返回
        return instance;
    }

}

假如说有多个线程来调 getInstance() 方法,instance = new Singleton() 又是一个写入的动作,对一个共享变量做并发写入一定会引发线程不安全的问题。所以这种方式实现的懒汉式一定是非线程安全的。


2. 线程安全

public class Singleton {

    // 在该类内部产生一个唯一的实例化对象,并且将其封装为 private static 类型的成员变量
    private static volatile Singleton instance = null;

    // 将构造方法私有化,使其不能在类的外部通过 new 关键字实例化该类对象
    private Singleton() {}

    // 定义一个静态方法返回这个唯一对象
    public static Singleton getInstance() {
        if (instance == null) {
            synchronized (Singleton.class) {
                // 判断实例是否为 null
                if (instance == null) {
                    // 创建一个实例
                    instance = new Singleton();
                }
            }
        }
        // 返回
        return instance;
    }

}

可以通过 synchronize 关键字将创建对象的这一段代码包起来,确保线程安全,当第一个线程拿到锁之后就会将实例创建出来,但是之后的线程还是会去竞争锁,然后再去判断 instance == null,这是没有必要的,所以在外层再进行一次 instance == null 的判断,让加锁的时机延后,减少不必要获取锁的次数,这就是是使用双检锁的方式实现线程安全懒汉式的写法。

这里还存在一个问题,instance 这个共享变量在多线程环境下是不具备可见性的,也就是说如果 A 线程已经把实例创建出来了,但是由于每个线程都有自己的工作缓存,导致 B 线程看到的 instance 可能依旧为 null,同样会进入到判断内,进行加锁的操作,也是没有必要的,为了使 instance 这里变量具有可见性 的特性,只需要加上 volatile 的关键字,使每个线程在获取该变量的时候都要在主内存中去获取,不能在缓存上去取。


参考博客:
单例模式 | 菜鸟教程:https://editor.csdn.net/md?not_checkout=1&articleId=130061430

你可能感兴趣的:(设计模式,单例模式,设计模式,java)