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

创建型-单例模式

了解单例

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

  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
}
相关推荐
Han.miracle12 小时前
数据结构——二叉树的从前序与中序遍历序列构造二叉树
java·数据结构·学习·算法·leetcode
Le1Yu13 小时前
分布式事务以及Seata(XA、AT模式)
java
寒山李白14 小时前
关于Java项目构建/配置工具方式(Gradle-Groovy、Gradle-Kotlin、Maven)的区别于选择
java·kotlin·gradle·maven
无妄无望15 小时前
docker学习(4)容器的生命周期与资源控制
java·学习·docker
MC丶科15 小时前
【SpringBoot 快速上手实战系列】5 分钟用 Spring Boot 搭建一个用户管理系统(含前后端分离)!新手也能一次跑通!
java·vue.js·spring boot·后端
千码君201615 小时前
React Native:从react的解构看编程众多语言中的解构
java·javascript·python·react native·react.js·解包·解构
夜白宋16 小时前
【word多文档docx合并】
java·word
@yanyu66617 小时前
idea中配置tomcat
java·mysql·tomcat
2501_9167665417 小时前
【项目部署】JavaWeb、MavenJavaWeb项目部署至 Tomcat 的实现方式
java·tomcat
RoboWizard17 小时前
扩容刚需 金士顿新款Canvas Plus存储卡
java·spring·缓存·电脑·金士顿