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

创建型-单例模式

了解单例

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

  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
}
相关推荐
驭渊的小故事12 小时前
MyBatis XML 开发实战:从配置到 CRUD 全解析
xml·java·mybatis
哦虎!17 小时前
【JavaEE】网络初识
java
for_ever_love__17 小时前
MySQL ORDER BY 排序优化:从 Using filesort 到索引有序扫描(含双路 / 单路排序原理)
java·mysql·优化·存储·by·order·filesort
我是人✓18 小时前
Java文件上传三方式对比:从传统到SpringBoot
java·开发语言
欣栀♡18 小时前
JVM 频繁 Full GC 先看日志括号里的 cause,jstat 的 FGC 在 JDK 8 上会骗人
java·jvm
IT研究室19 小时前
最新计算机毕业设计选题推荐-基于spring boot的葡萄酒文化宣传网站-网站-文档指导-Java-springboot
java·spring boot·课程设计
慧都小项19 小时前
MyEclipse 中创建和测试 JAX-RS REST 接口:从项目配置到 REST Explorer
java·restful·myeclipse·jax-rs
Wang's Blog19 小时前
Java 项目实战: 交友项目 - 入门案例:编写 Dubbo 服务提供者
java·dubbo·交友
秋饼19 小时前
Spring AI 2.0 结构化输出企业级实战:从 BeanOutputConverter 源码到生产级解析容错
java·ai·技术分享·后端开发
摇滚侠19 小时前
《计算机是如何工作的:人人都能懂的计算机软硬件工作原理》第 9 章 机器码与汇编语言 阅读笔记 9
java·开发语言·笔记