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

创建型-单例模式

了解单例

单例模式是一种创建型设计模式,它提供了一种创建对象的最佳方式;它必须保证:

  1. 单例类只能有一个实例化对象;
  2. 单例类必须创建自己的唯一实例;
  3. 单例类必须给其他对象提供实例;

另外:

  1. 它的目的是:确保一个类只用一个实例,并提供一种全局访问入口来访问该实例
  2. 设计思想:在获取实例的时候判断实例是否存在,如果存在,则直接返回,如果不存在则创建实例;
  3. 关键代码:构造方法私有化;

角色

  1. 单例类:包含单例实例的类
  2. 静态成员变量:用于存储单例的静态成员变量,final修饰防止被继承
  3. 获取实例方法:静态方法,用于获取单例实例
  4. 私有构造方法:防止外部直接实例化类
  5. 线程安全处理:确保多线程环境下单例创建的安全性

实现方式

饿汉式单例

特点:类一加载就实例化单例对象

java 复制代码
public class Mgr01 {
    //静态成员变量存储单例,final修饰防止被继承
    private final static Mgr01 INSTANCE = new Mgr01();

    //构造方法私有化,防止外部直接实例化
    private Mgr01() {
    }
    //静态方法,用于获取单例
    public static Mgr01 getInstance(){
        return INSTANCE;
    }
}

另一种写法,在静态代码块中实例化对象

java 复制代码
public class Mgr02 {
    private final static Mgr02 INSTANCE ;
    static {
        INSTANCE = new Mgr02();
    }
    public static Mgr02 getMgr02() {
        return INSTANCE;
    }
}

懒汉式单例

特点: 使用单例时才实例化对象

线程不安全写法:

java 复制代码
    public class Mgr03 {
        private static Mgr03 INSTANCE;
        private Mgr03() {
        }

        public static Mgr03 getInstance(){
            //模拟执行其他操作所用的时间
            if( INSTANCE == null){
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
                INSTANCE = new Mgr03();
            }
            return INSTANCE;
        }
}

synchronized锁获取实例静态方法,保证线程安全:

java 复制代码
public class Mgr04 {
    private static Mgr04 INSTANCE;
    private Mgr04() {
    }
    public static synchronized Mgr04 getInstance(){
        //模拟执行其他操作所用的时间
        if( INSTANCE == null){
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            INSTANCE = new Mgr04();
        }
        return INSTANCE;
    }
}

双重检验锁(DCL)单例:

  1. 第一层null值检测是为了在已经存在单例的情况下不需要等锁提高效率,第二次null判断是为了保证单例。
  2. volatile关键字的作用:如果不使用volatile关键字那么,创建单例过程可能被拆分为以下几步,①为单例对象分配内存空间;②初始化单例对象;③将INSTANCE变量指向分配的内存空间。在没有volatile 关键字的情况下,步骤②和③可能会被重排序。这就可能导致其他线程在执行getInstance() 方法时,看到的 INSTANCE 变量已经被赋值,但单例对象并没有被完成初始化。
java 复制代码
public class Mgr06 {
    private static volatile Mgr06 INSTANCE;//volatile 是为了防止JVM中语句重排
    private Mgr06() {
    }
    public static  Mgr06 getInstance(){
        //这个判断可以屏蔽很多操作,很多线程到这,如果已INSTANCE已经存在,可以减少下面代码的执行,提升效率
        if( INSTANCE == null){
            synchronized (Mgr06.class){
                if(INSTANCE == null){
                    try {
                        Thread.sleep(1);
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }
                    INSTANCE = new Mgr06();
                }
            }

        }
        return INSTANCE;
    }
}

静态内部类单例

这种方法是通过JVM保证单例,JVM在加载外部类时,只加载一次,且内部类在使用时才会加载,也就是第一次调用获取实例的方法时候才会调用。

其中,内部类私有化,内部类中的静态变量也私有化;

java 复制代码
public class Mgr07 {
    private static class MGR_07{
        private final static Mgr07 INSTANCE = new Mgr07();
    }
    public static Mgr07 getInstance(){
        return MGR_07.INSTANCE;
    }
}

枚举单例

枚举单例不但可以保证单例,还可以防止反序列化,因为枚举没有构造方法。

java 复制代码
public enum Mgr08 {
    INSTANCE
}
相关推荐
Hello.Reader2 小时前
Data Sink定义、参数与可落地示例
java·前端·网络
2401_837088503 小时前
stringRedisTemplate.opsForHash().entries
java·redis
lkbhua莱克瓦244 小时前
Java基础——集合进阶3
java·开发语言·笔记
蓝-萧5 小时前
使用Docker构建Node.js应用的详细指南
java·后端
多喝开水少熬夜5 小时前
Trie树相关算法题java实现
java·开发语言·算法
执笔论英雄5 小时前
【大模型训练】加载load_state 中的一些技巧 工厂设计模式
设计模式
lkbhua莱克瓦245 小时前
Java基础——集合进阶用到的数据结构知识点1
java·数据结构·笔记·github
音符犹如代码6 小时前
Java并发List实战:CopyOnWriteArrayList原理与ArrayList常见面试题
java·开发语言·面试·list
代码or搬砖6 小时前
Docker 部署 Java 项目实践
java·docker·容器
又是忙碌的一天7 小时前
抽象类和接口
java·开发语言