设计模式--单例模式

150 阅读4分钟

设计模式--单例模式

简介

特点

这种模式涉及到一个单一的类,该类负责创建自己的对象,同时确保只有单个对象被创建。这个类提供了一种访问其唯一的对象的方式,可以直接访问,不需要实例化该类的对象。

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

主要解决:一个全局使用的类频繁地创建与销毁。

何时使用:当您想控制实例数目,节省系统资源的时候。

如何解决:判断系统是否已经有这个单例,如果有则返回,如果没有则创建。

关键代码:构造函数是私有的。

优点:

1、在内存里只有一个实例,减少了内存的开销,尤其是频繁的创建和销毁实例(比如管理学院首页页面缓存)。 2、避免对资源的多重占用(比如写文件操作)。

缺点:

没有接口,不能继承,与单一职责原则冲突,一个类应该只关心内部逻辑,而不关心外面怎么样来实例化。

实现

单例模式有懒汉式、饿汉式、双层校验锁模式,额外的:ENUM 枚举也是一种单例

饿汉式(线程安全)

/**
 * @className Singleton1
 * @author:yadmire
 * @create: 2022-05-15 16:17
 * @Description: 饿汉式单例 Demo
 * 优点:没有加锁,执行效率会提高。
 * 缺点:类加载时就初始化,浪费内存
 */
public class Singleton1 {
    //私有化构造,不允许新建对象
    private Singleton1(){
    }
    private static Singleton1 INSTANCE=new Singleton1();

    /**
     * 返回对象
     * @return
     */
    public Singleton1 getSigleton(){
        return INSTANCE;
    }

}

懒汉式-线程不安全(有缺陷,不可取)

package com.yadmire.design.ceational.singleton;

/**
 * @className Singleton2
 * @author: yadmire
 * @create: 2022-05-15 16:24
 * @Description: 懒汉式 (在第一次调用的时候实例化自己)
 *  这种方式是最基本的实现方式,这种实现最大的问题就是不支持多线程。因为没有加锁 synchronized,所以严格意义上它并不算单例模式。
 */
public class Singleton2 {
    //私有化构造,不允许新建对象
    private Singleton2(){
    }
    private static Singleton2 INSTANCE;
    /**
     * 返回对象
     * @return
     */
    public Singleton2 getSigleton(){
        // 首次加载时进行创建
        if(INSTANCE==null){
            INSTANCE=new Singleton2();
        }
        return INSTANCE;
    }
}

懒汉式-线程安全

package com.yadmire.design.ceational.singleton;

/**
 * @className Singleton3
 * @author: yadmire
 * @create: 2022-05-15 16:29
 * @Description: 懒汉式-加锁,线程安全
 */
public class Singleton3 {
    // 私有化构造
    private Singleton3 (){}
    private static Singleton3 INSTANCE=null;

    // 加锁,保证单例,但是比较重,影响效率
    public  synchronized Singleton3 getSingleton3(){
        if(INSTANCE==null){
            INSTANCE=new Singleton3();
        }
        return INSTANCE;
    }
}

懒汉式--双检锁

package com.yadmire.design.ceational.singleton;

/**
 * @className Singleton4
 * @author: yadmire
 * @create: 2022-05-15 16:34
 * @Description: 饿汉式-双重检查机制
 */
public class Singleton4 {

    // 这里添加 volatile 保证创建过程是线程可见的
    // new 操作在字节码层面上会分解成3个操作:
    // 1. 给 singleton 分配内存
    // 2. 调用 Singleton 的构造函数来初始化成员变量,形成实例
    // 3. 将singleton对象指向分配的内存空间(执行完这步 singleton才是非 null了)
    // 通过添加 volatile (内存屏障)保证线程的可见性与顺序性,同时保证不会发生指令重排,
    // 但是此处保证的不是new 时的指令重排,而是保证在写操作时,通过内存屏障,为该内存区域加锁,保证不会有读操作,保证写期间不会有读该内存区域的操作
    private  static volatile Singleton4 INSTANCE= null;

    private Singleton4(){}

    public static Singleton4 getINSTANCE() {
        if(INSTANCE == null){
            // 局部加锁,增加效率
            synchronized (Singleton4.class){
                if(INSTANCE ==null){
                    INSTANCE=new Singleton4();
                }
            }
        }
        return INSTANCE;
    }
}

枚举

枚举本身也是单例的,比如,如下的验证:

/**
 * @className Singleton5
 * @author: yadmire
 * @create: 2022-05-15 16:58
 * @Description: 额外:枚举类型
 */
public enum  Singleton5 {
    SINGLETON_5;
    private String name;
    public void setName(String name){
        this.name = name;
    }
    public String getName(){
        return name;
    }

}

验证代码如下:

public class SingletonTest {

    @Test
    public void testSingletonEnum(){
        System.out.printf("test for Singleton Enum");
        Singleton5 singleton1 = Singleton5.SINGLETON_5;//枚举类型实例化(静态块中)
        singleton1.setName("单例1");
        System.out.println(singleton1.getName());

        Singleton5 singleton2 = Singleton5.SINGLETON_5;
        singleton2.setName("单例2");
        System.out.println(singleton2.getName());

        System.out.println(singleton1.getName() + ":" + singleton2.getName());

        // 断言 两者是同一个对象
        Assert.assertTrue(singleton1==singleton2);
    }
}