23模式---单例模式

news2024/11/18 23:02:21

单例模式,属于创建类型的一种常用的软件设计模式。通过单例模式的方法创建的类在当前进程中只有一个实例(根据需要,也有可能一个线程中属于单例,如:仅线程上下文内使用同一个实例)

这个也是23设计模型中最简单理解的一种模型,而本篇就简单聊一下这个模型。

这个模型创建单例在Java中两种方式:

  • 饿汉式:其就是在创建类的时候就创建对象。
  • 懒汉式:其是在需要用对象的时候才创建。

这个是单例模型中的两种方式,但是具体实现的时候又有划分了,用不同的方式创建的单例模式可以有7种范式。

1:饿汉式—静态常量

先看代码:

public class test {
    public static void main(String[] args) {
        SingleInstance  s1=SingleInstance.getSingleInstance();
        SingleInstance  s2=SingleInstance.getSingleInstance();
        System.out.println(s1==s2);// true

    }
}

class SingleInstance{
//   如果有可能带有参数,所以创建一个无参构造函数
    public  SingleInstance(){}
//    饿汉模式,就是在创建类的时候就创建对象,所以带有关键字static
    private final static SingleInstance singleInstance=new SingleInstance();
// 提供一个返回单例的静态方法
    public static   SingleInstance getSingleInstance(){
        return singleInstance;
    }
}

  • 优点
    • 写法简单,在类加载的时候就完成了实例化,避免了线程同步的问题。(不同实例化static的,可以看一下我java基础中文章)
  • 缺点:
    • 类装载的时候就完成了实例化,如果不用的话机会造成内存的浪费,所以这一方面不如懒汉模式好,毕竟懒汉模式是使用的时候才调用。

2:饿汉式—静态代码块

其实这个和静态常量,区别不大,只不过static关键字放的位置不同,如下:

public class test {
    public static void main(String[] args) {
        SingleInstance s1 = SingleInstance.getSingleInstance();
        SingleInstance s2 = SingleInstance.getSingleInstance();
        System.out.println(s1 == s2);// true

    }
}

class SingleInstance {
    //   如果有可能带有参数,所以创建一个无参构造函数
    public SingleInstance() {
    }
    private static SingleInstance singleInstance;
    //    饿汉模式, 创建的对象可以通过代码块创建
    static {
        SingleInstance singleInstance = new SingleInstance();
    }
    
    // 提供一个返回单例的静态方法
    public static SingleInstance getSingleInstance() {
        return singleInstance;
    }
}

其实和饿汉模式的静态常量的优缺点一样的:

  • 优点
    • 写法简单,在类加载的时候就完成了实例化,避免了线程同步的问题。(不同实例化static的,可以看一下我java基础中文章)
  • 缺点:
    • 类装载的时候就完成了实例化,如果不用的话机会造成内存的浪费,所以这一方面不如懒汉模式好,毕竟懒汉模式是使用的时候才调用。

3:懒汉式–线程不安全

public class test {
    public static void main(String[] args) {
        SingleInstance s1 = SingleInstance.getSingleInstance();
        SingleInstance s2 = SingleInstance.getSingleInstance();
        System.out.println(s1 == s2);// true

    }
}

class SingleInstance {
    private static SingleInstance singleInstance;
    //   如果有可能带有参数,所以创建一个无参构造函数
    public SingleInstance() {
    }



    // 提供一个返回单例的静态方法
    public static SingleInstance getSingleInstance() {
//        判断一些是否已创建过,不然每次调用的时候都会创建一次
        if(singleInstance==null){
            singleInstance=new SingleInstance();
        }
        return  singleInstance;
    }
}

看起结果似乎没有问题,但是如果的多线程调用的单例模式就返回的不是同一个对象了:

public class test   {

    public static void main(String[] args) {

//       多运行几次,因为运行时间很多,有可能返回相同的hashcode
        ExecutorService threadPool= Executors.newFixedThreadPool(3);

        threadPool.execute(()->{
            System.out.println(SingleInstance.getSingleInstance());
        });
        threadPool.execute(()->{
            System.out.println(SingleInstance.getSingleInstance());
        });
        threadPool.shutdown();

    }


}

class SingleInstance {
    private static SingleInstance singleInstance;
    //   如果有可能带有参数,所以创建一个无参构造函数
    public SingleInstance() {
    }



    // 提供一个返回单例的静态方法
    public static SingleInstance getSingleInstance() {
//        判断一些是否已创建过,不然每次调用的时候都会创建一次
        if(singleInstance==null){
            singleInstance=new SingleInstance();
        }
        return  singleInstance;
    }
}

在这里插入图片描述

可以看出虽然式单例模式,但是其返回的实例不是同一个。那单例模式还是单例模式吗?

总结:

  • 优点:

    • 起到了懒汉模式的效果,就是在调用的时候创建实例。
  • 缺点:

    • 只能在单线程下其效果,但是其在多线程下有可能就是无效的单例模式了。

所以不推荐这种方式,毕竟有同步问题有问题。

4:懒汉式—线程安全

既然上面的同步不安全,那就再方法上加入synchronized进行修饰。

public class test   {

    public static void main(String[] args) {
        ExecutorService threadPool= Executors.newFixedThreadPool(3);

        threadPool.execute(()->{
            System.out.println(SingleInstance.getSingleInstance());
        });
        threadPool.execute(()->{
            System.out.println(SingleInstance.getSingleInstance());
        });
        threadPool.shutdown();

    }


}

class SingleInstance {
    private static SingleInstance singleInstance;
    //   如果有可能带有参数,所以创建一个无参构造函数
    public SingleInstance() {
    }



    // 提供一个返回单例的静态方法
    public static synchronized SingleInstance getSingleInstance() {
//        判断一些是否已创建过,不然每次调用的时候都会创建一次
        if(singleInstance==null){
            singleInstance=new SingleInstance();
        }
        return  singleInstance;
    }
}

然后多次运行,发现了运行的是同一个对象。

在这里插入图片描述

总结:

  • 优点:
    • 解决了多线程不安全问题,无论多线程还是单线程都可以使用了。
  • 缺点:
    • 效率很低,多线程 执行get方法的时候,都会被synchronized锁住,所以其效率会低。

不推荐这种方式,毕竟效率会降低。

5:懒汉式—同步代码块

既然上面一种线程安全导致的效率低,那么这样操作呢?

public class test   {

    public static void main(String[] args) {

//        System.out.println(s1 == s2);// true
        ExecutorService threadPool= Executors.newFixedThreadPool(3);

        threadPool.execute(()->{
            System.out.println(SingleInstance.getSingleInstance());
        });
        threadPool.execute(()->{
            System.out.println(SingleInstance.getSingleInstance());
        });
        threadPool.shutdown();

    }


}

class SingleInstance {
    private static SingleInstance singleInstance;
    //   如果有可能带有参数,所以创建一个无参构造函数
    public SingleInstance() {
    }


    // 提供一个返回单例的静态方法
    public static  SingleInstance getSingleInstance() {
//        判断一些是否已创建过,不然每次调用的时候都会创建一次
        if(singleInstance==null){
        
            synchronized (SingleInstance.class) {
                singleInstance = new SingleInstance();
            }
        }
        return  singleInstance;
    }
}

先看运行的结果:

在这里插入图片描述

其实用同步锁住SingleInstance.class,这个想法是没有错的,这样就锁住了同时创建对象。但是 虽然锁住了SingleInstance.class,但是两个线程同时运行到if的时候已经进来了,只不过是在创建对象到时候不能同事通过类创建对象,但是还是会创建两个对象。同时比不带 synchronized (SingleInstance.class) 的运行理论上还要慢,当然都不能保证返回一个对象。优缺点也一样,不再重复说了。

所以这种方式,在开放过程中根本不会用。

6:懒汉式–双重检测同步代码块

public class test   {

    public static void main(String[] args) {

//        System.out.println(s1 == s2);// true
        ExecutorService threadPool= Executors.newFixedThreadPool(3);

        threadPool.execute(()->{
            System.out.println(SingleInstance.getSingleInstance());
        });
        threadPool.execute(()->{
            System.out.println(SingleInstance.getSingleInstance());
        });
        threadPool.shutdown();

    }


}

class SingleInstance {
    private static SingleInstance singleInstance;
    //   如果有可能带有参数,所以创建一个无参构造函数
    public SingleInstance() {
    }

    // 提供一个返回单例的静态方法
    public static  SingleInstance getSingleInstance() {
        //  第一次判断
        if(singleInstance==null){
            synchronized (SingleInstance.class) {
                 //  第二次判断
                if(singleInstance==null) {
                    singleInstance = new SingleInstance();
                }
            }
        }
        return  singleInstance;
    }
}

这种方式,保证了同步线程的安全,同时其效率要比4:懒汉式—线程安全效率要高,毕竟if判断要比完全的锁住get方法开支要少。

在实际开发过程中推荐使用这种方式,来实现懒汉式。

7:懒汉式–静态内部类实现

public class test   {

    public static void main(String[] args) {

//        System.out.println(s1 == s2);// true
        ExecutorService threadPool= Executors.newFixedThreadPool(3);

        threadPool.execute(()->{
            System.out.println(Single.getInstance());
        });
        threadPool.execute(()->{
            System.out.println(Single.getInstance());
        });
        threadPool.shutdown();
    }
}

class Single{
    // 利用内部静态类的加载属性
    private static class SingleInstance {
        private static final Single  INSTANCE=new Single();
    }
    public static  Single getInstance(){
        return  SingleInstance.INSTANCE;
    }

}

在这里插入图片描述

这种方式采用了类装载的机制来保证初始化实例时只有一个线程。当类被使用时候静态属性,静态方法,以及静态代码块会被实例化。但是静态内部类(SingleInstance)不会被在外部类(Single)不会被立即实例化,只有在调用getInstance方法的时候,才会触发静态内部类,而这个时候也会在第一次的时候实例化其静态属性。而这个是jvm保证了线程的安全,也就是进行初始化的时候别的线程是无法进入的。

其满足了懒汉模式同时其多线程下也是同一个实例,同样也比第5中方式效率要高,所以开发过程中推荐这种方式。

8:懒汉式—枚举实现

public class test   {

    public static void main(String[] args) {
        SingleInstance singleInstance=SingleInstance.INSTANCE;
        singleInstance.sayhello();

        ExecutorService threadPool= Executors.newFixedThreadPool(3);

        threadPool.execute(()->{
            System.out.println(SingleInstance.INSTANCE);
        });
        threadPool.execute(()->{
            System.out.println(SingleInstance.INSTANCE);
        });
        threadPool.shutdown();
    }
    
}

enum SingleInstance{
    INSTANCE;
    //也可以带有枚举中的方法
    public void sayhello(){
        System.out.println("testttttttt");

    }

}

在这里插入图片描述

这个其实用来枚举的特性,因为枚举属性是不可变的,所以其可以避免了多线程同步问题,而且还能防止反序列化重新创建对象,这个也是一些大神推荐的方式,所以这中方式推荐使用。

总结

单例模式保证了系统内存中该类只有一个对象,节省了系统自由,对于一些需要频繁创建销毁的对象,使用单例模式可以提供系统性能。比如:工具类对象,频繁访问数据库或文件对象(比如数据源头,session工厂等)

在选择饿汉式和懒汉式的时候,需要明白一件事情,如果对于某个对象不确定是否会调用那就使用懒汉式,但是如果确定其必然会调用一次,那么直接可以使用饿汉式了。所以两种模式具体使用那种,还是根据自己的业务的需求。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/816.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Python 图像处理OpenCV:几何变换(笔记)

包括图像缩放、图像平移、图像旋转、图像的仿射变换、图像的透射变换及图像金字塔等内容。 图像缩放: 缩放是对图像的大小进行调整,即使图像放大或缩小。cv2.resize(src,dsize,fx0,fy0,interpolationcv2.INTER_LINEAR)src : 输入图像dsize: 绝对尺寸&a…

Linux运维面试题总结—Linux基础、计算机网络基础

文章目录一、三次握手四次挥手二、如何划分vlan三、为什么划分vlanvlan三个模式:vxlan和vlan区别是什么?四、OSI七层模型及对应协议五、Linux中 查找大于10M的文件并删除六、查看cup占用情况,查看内存,查看磁盘IO使用情况&#xf…

图像处理黑科技——弯曲矫正、去摩尔纹、切边增强、PS检测

目录0 前言1 弯曲矫正2 去摩尔纹3 图像切边增强4 PS检测5 总结0 前言 合合信息是行业领先的人工智能及大数据科技企业,专注文字识别领域16年,在智能文字识别及商业大数据等核心领域处于国内领先地位,全球企业和个人用户提供创新的数字化、智…

代码随想录动态规划——一和零

题目 给你一个二进制字符串数组 strs 和两个整数 m 和 n 。 请你找出并返回 strs 的最大子集的大小,该子集中 最多 有 m 个 0 和 n 个 1 。 如果 x 的所有元素也是 y 的元素,集合 x 是集合 y 的 子集 。 示例 1: 输入:strs [“10…

Oracle Unifier 系统架构简述(安装部署)

关于Oracle Primavera Unifier 的应用架构,其实在我之前的博客已有介绍相关内容 谈谈 Oracle P6 , Unifier 和其他应用系统间的联系https://campin.blog.csdn.net/article/details/104972949 从官方文档方面,其实在《unifier_performance_and_sizing_g…

【C语言】全面解析数据在内存中的存储

文章目录前言类型的基本分类整型浮点数自定义类型整型在内存中的存储原码、反码、补码大端和小端如何判断编译器是大端还是小端浮点数在内存中的存储总结前言 C语言中有char、short、int、long、long long、float和doubole这些数据类型。这些数据类型也叫内置类型。 所占存储空…

JECloud微服务低代码平台重大发布。

JECloud微服务低代码平台【1.0.0】版升级发布,本次发布内容如下: JECloud微服务低代码平台是一款基于元数据领域模型构建的低代码开发平台,其底层采用微服务与微应用构建底层框架,并基于基础框架构建各核心微服务模块来实现低代码…

手撕七大排序 (四)

归并排序归并排序递归写法一. 基本概念二. 图解归并排序递归写法 一. 基本概念 归并排序(MERGE-SORT)是建立在归并操作上的一种有效的排序算法,该算法是采用分治法(Divide andConquer)的一个非常典型的应用。将已有序的子序列合并…

数据结构与算法——链表(双向链表,顺序表与链表的比较)

🍓个人主页:bit.. 🍒系列专栏:Linux(Ubuntu)入门必看 C语言刷题 数据结构与算法 目录 一.双向链表 二.双向链表的对称性:(设指针p指向某一结点) 1.双向链表的插入 2.双向链表的删除 …

浏览器中的页面循环系统

【】渲染进程中有个主线程处理安排好的任务,为了在线程运行过程中接收并处理新任务,引入了事件循环机制;为了处理其他线程发送来的任务,引入消息队列;为了处理其他进程发送来的任务,渲染进程专门有一个IO线…

【数据结构初阶】二、顺序表的实现

目录 一、线性表 二、顺序表 2.1 顺序表概念及结构 2.2 顺序表接口实现 2.2.1 顺序表初始化 2.2.2 顺序表的销毁 2.2.3 顺序表的打印 2.2.4 顺序表增加数据(插入,头插、尾插) 2.2.5 顺序表删除数据(删除,头删…

Uniapp集成腾讯IM+音视频通话

腾讯IM(包含界面)源码下载相关配置 传送门:https://cloud.tencent.com/document/product/269/36887 传送门:https://github.com/TencentCloud/TIMSDK/tree/master/uni-app vue2 vue3都可 笔者用的vue2 解压文件 拖到编辑器 #项目右键 在命令行窗口打开 …

Promethues原理详解

目录 引言 一、Prometheus概念 1.1、什么是Prometheus 1.2、Zabbix和Prometheus区别 1.3、Prometheus的特点 二、运维监控平台设计思路 三、Prometheus监控体系 3.1、系统层监控(需要监控的数据) 3.2、中间件及基础设施类监控 3.3、应用层监控…

语音合成经典模型结构介绍

(以下内容搬运自 PaddleSpeech) Models introduction TTS system mainly includes three modules: Text Frontend, Acoustic model and Vocoder. We introduce a rule-based Chinese text frontend in cn_text_frontend.md. Here, we will introduce acoustic models and voc…

XDataverse免费的统一数据库管理工具

XDataverse产品简介 XDataverse是一款通用的数据库管理工具,主要管理关系型数据库,同时也支持一些其余类型的数据库,比如Redis。其主要功能有 支持主流关系型数据库的常规操作,比如MySQL,SQLServer,SQlite,SQLCE,Postg…

机器学习 逻辑回归(2)softmax回归多类别分类-鸢尾花案例

机器学习 逻辑回归之softmax回归多类别分类-鸢尾花案例一、前言二、假设函数三、One-Hot 独热编码四、代价函数五、梯度下降六、原生代码实现6.1 加载并查看数据6.2 添加前置与数据分割6.3 迭代训练6.4 验证数据七、sklearn代码实现八、参考资料PS:softmax回归损失函…

[时间序列预测]基于BP、LSTM、CNN-LSTM神经网络算法的单特征用电负荷预测[保姆级手把手教学]

系列文章目录 深度学习原理-----线性回归梯度下降法 深度学习原理-----逻辑回归算法 深度学习原理-----全连接神经网络 深度学习原理-----卷积神经网络 深度学习原理-----循环神经网络(RNN、LSTM) 时间序列预测-----基于BP、LSTM、CNN-LSTM神经网络…

安卓开发Android studio学习笔记14:用户注册登录(案例演示)

Android studio学习笔记第一步:配置activity_information.xml第二步:配置activity_registration.xml第三步:配置strings.xml第四步:配置InformationActivity第五步:配置RegistrationActivity第六步:运行结果…

二叉搜索树

文章目录二叉搜索树1. 概念2. 模拟实现二叉搜索树2.1 准备工作 创建类2.2 查找方法2.3 插入方法2.4 删除方法3. 性能分析二叉搜索树 前言 : 1. 概念 二叉搜索树又称二叉排序树,它或者是一棵空树,或者是具有以下性质的二叉树: 若它的左子树不…

学点高端技术:基于密度的聚类算法——FDBSCAN算法

机器学习、人工智能各类KNN算法层出不穷,DBSCAN具有强代表性,它是一个基于密度的聚类算法,最大的优点是能够把高密度区域划分为簇,能够在高噪声的条件下实现对目标的精准识别,但该算法当前已远不能满足人们对于高效率、…