设计模式 -- 单例设计模式

1.1 单例

创建一个单例对象 SingleModel , SingleModel 类有它的私有构造函数和本身的一个静态实例。
SingleModel 类提供了一个静态方法,供外界获取它的静态实例。 DesignTest 我们的演示类使用
SingleModel 类来获取 SingleModel 对象。
创建 SingleModel :

java 复制代码
public class SingleModel {
//创建 SingleModel 的一个对象
private static SingleModel instance = new SingleModel();
//让构造函数为 private,这样该类就不会被实例化
private SingleModel(){}
//获取唯一可用的对象
public static SingleModel getInstance(){
return instance;
}
public void useMessage(){
System.out.println("Single Model!");
}
}

测试:

java 复制代码
public class DemoTest {
/****
* 单例模式测试
*/
@Test
public void testSingleModel(){
//不合法的构造函数
//编译时错误:构造函数 SingleModel() 是不可见的
//SingleModel singleModel = new SingleModel();
//获取唯一可用的对象
SingleModel singleModel1 = SingleModel.getInstance();
SingleModel singleModel2 = SingleModel.getInstance();
//显示消息
singleModel1.useMessage();
//创建的2个对象是同一个对象
System.out.println(singleModel1 == singleModel2);
}
}

输入结果如下:
Single Model!
true
我们测试创建 10 万个对象,用单例模式创建,仅占内存: 104 字节,而如果用传统方式创建 10 万个对
象,占内存大小为 2826904 字节。

1.2 扩展

单例模式有多种创建方式,刚才创建方式没有特别的问题,但是程序启动就需要创建对象,不管你用不
用到对象,都会创建对象,都会消耗一定内存。因此在单例的创建上出现了多种方式。
懒汉式:
1 、延迟加载创建,也就是用到对象的时候,才会创建
2 、线程安全问题需要手动处理 ( 不添加同步方法,线程不安全,添加了同步方法,效率低 )
3 、实现容易
案例如下: SingleModel1

java 复制代码
 public class SingleModel1 {
        private static SingleModel1 instance;

        private SingleModel1(){}

        public static SingleModel1 getInstance(){
            if(instance == null){
                instance = new SingleModel1();
            }
            return instance;
        }
    }

如果在创建对象实例的方法上添加同步 synchronized , 但是每次 get 获取对象都必须排队竞争,效率极
低,代码如下:
添加同步会解决多线程安全的问题但是会明显的降低获取对象实例的效率。

java 复制代码
public static synchronized SingleModel1 getInstance() {
        
        if (instance == null){
            instance = new SingleModel1();
        }
        return instance;
    }

双重校验锁: SingleModel2
这种方式采用双锁机制,安全且在多线程情况下能保持高性能。

java 复制代码
public class SingleModel2 {
        //不实例化
        private static SingleModel2 instance;
        //让构造函数为 private,这样该类就不会被实例化
        private SingleModel2(){}
        //获取唯一可用的对象
        public static SingleModel2 getInstance(){
//instance为空的时候才创建对象
            if(instance==null){
//同步锁,效率比懒汉式高
                synchronized (SingleModel2.class){
//这里需要判断第2次为空
                    if(instance==null){
                        instance = new SingleModel2();
                    }
                }
            }
            return instance;
        }
        public void useMessage(){
            System.out.println("Single Model!");
        }
    }

指令重排问题
对象创建,一般正确流程如下:

  1. 申请内存空间
  2. 创建并初始化对象
  3. 将变量指向申请的内存空间地址

但其实在对象创建的时候,也有可能发生 指令重排问题,也就是上面流程会被打乱:

  1. 申请内存空间
  2. 将变量指向申请的内存空间地址 //这一步后,变量不再为null,但是初始化其实并没完成
  3. 创建并初始化对象

如果是这样的话,双检锁在多线程情况下也会出现问题,需要添加 volatile 属性,该属性能防止指令
重排,代码如下:

java 复制代码
    public class SingleModel2 {
        //不实例化
        private static volatile SingleModel2 instance;
        //让构造函数为 private,这样该类就不会被实例化
        private SingleModel2(){}
        //获取唯一可用的对象
        public static SingleModel2 getInstance(){
//instance为空的时候才创建对象
            if(instance==null){
//同步锁,效率比懒汉式高
                synchronized (SingleModel2.class){
//这里需要判断第2次为空
                    if(instance==null){
                        instance = new SingleModel2();
                    }
                }
            }
            return instance;
        }
        public void useMessage(){
            System.out.println("Single Model!");
        }
    }

1.3 单例模式特点

  1. 单例类只能有一个实例。 A a = new A()
  2. 单例类必须自己创建自己的唯一实例。
  3. 单例类必须给所有其他对象提供这一实例。

1.4单例模式优点:

  1. 在内存里只有一个实例,减少了内存的开销,尤其是频繁的创建和销毁实例。
  2. 避免对资源的多重占用(比如写文件操作)。
相关推荐
考虑考虑18 小时前
Jpa使用union all
java·spring boot·后端
用户37215742613519 小时前
Java 实现 Excel 与 TXT 文本高效互转
java
浮游本尊20 小时前
Java学习第22天 - 云原生与容器化
java
渣哥21 小时前
原来 Java 里线程安全集合有这么多种
java
间彧1 天前
Spring Boot集成Spring Security完整指南
java
间彧1 天前
Spring Secutiy基本原理及工作流程
java
数据智能老司机1 天前
精通 Python 设计模式——创建型设计模式
python·设计模式·架构
Java水解1 天前
JAVA经典面试题附答案(持续更新版)
java·后端·面试
数据智能老司机1 天前
精通 Python 设计模式——SOLID 原则
python·设计模式·架构
洛小豆1 天前
在Java中,Integer.parseInt和Integer.valueOf有什么区别
java·后端·面试