单例模式:饿汉模式、懒汉模式

目录

一、什么是单例模式

二、饿汉模式

三、懒汉模式


一、什么是单例模式

单例模式是Java中的设计模式之一,能够保证某个类在程序中只存在唯一一份实例,而不会创建出多个实例

单例模式有很多实现方式,最常见的是饿汉和懒汉两种模式

二、饿汉模式

饿汉模式在类加载时就创建实例

如何实现饿汉式单例模式?

1. 在类中完成实例的初始化,在创建的类中创建唯一实例

2. 对外提供获取该唯一实例的方法,提供访问该实例的全局静态方法getInstance(),来获取该类的唯一实例

3. 构造方法私有化,保证类外部不能实例化,只有在类中创建的唯一实例

//饿汉式单例模式
public class HungrySingLeton {
    //在类中创建唯一实例
    private static final HungrySingLeton instance = new HungrySingLeton();
    
    //构造方法私有化,保证类外部不能进行实例化
    private HungrySingLeton(){}

    //对外提供获取该唯一实例的方法
    public static HungrySingLeton getInstance(){
        return instance;
    }
}

饿汉式单例模式线程安全吗?

饿汉式单例模式在类加载时就进行初始化,创建唯一实例。它在线程还没出现之前就实例化了,外部只能通过getInstance()方法来获取唯一实例,相当于“读操作”,因此是线程安全的

饿汉式单例模式的缺点

在类加载时就创建实例,并一直在内存中,若不使用该实例,该实例仍然存在,此时存在内存浪费问题

三、懒汉模式

类加载时不创建实例,直到第一次使用的时候才创建实例

如何实现懒汉模式?

懒汉模式的实现与饿汉模式类似,唯一的区别是懒汉模式直到第一次使用的时候才会创建实例

1. 在类中创建唯一实例,并将该实例的初始值设为null

2. 对外提供获取该唯一实例的方法,若是第一次使用该方法,则创建实例

3.  构造方法私有化,保证类外部不能实例化,只有在类中创建的唯一实例

public class LazySingleton {
    //在类中创建唯一实例,并将其置为null
    private static LazySingleton instance = null;
    
    //构造方法私有化,保证类外部不能进行实例化
    private LazySingleton(){}
    
    //对外提供获取该唯一实例的方法
    public static LazySingleton getInstance(){
        //若是第一次使用该方法,则初始化instance
        if (instance == null){
            instance = new LazySingleton();
        }
        return instance;
    }
    
}

懒汉模式线程安全吗?

 在多线程情况下,可能会出现创建多个实例的情况

如何解决线程安全问题?

通过加锁,来解决线程安全问题

判断instance是否为空,和创建实例两个操作加上锁,或是直接在方法上加上synchronized,从而保证在上图的情况下,也只创建一个实例

public static LazySingleton getInstance(){
        synchronized (LazySingleton.class){
            //若是第一次使用该方法,则初始化instance
            if (instance == null){
                instance = new LazySingleton();
            }
        }
        return instance;
    }

 或

public synchronized static LazySingleton getInstance(){
        //若是第一次使用该方法,则初始化instance
        if (instance == null){
            instance = new LazySingleton();
        }
        return instance;
    }

 此时

由于加锁和解锁开销较高,而懒汉式单例模式仅在第一次调用时,才会存在可能创建多个实例的问题,在后面调用getInstance()方法时,判断instance不为空,直接返回instance,而在加锁后,无论是否已经存在实例,在多线程情况下都会发生阻塞,此时存在执行效率低的问题

因此,在加锁前,判断是否已经创建实例,若已经创建实例,则直接返回instance,若未创建实例,则进行加锁操作

 public static LazySingleton getInstance(){
        //判断是否已经创建实例,若已经创建实例,则不加锁,直接返回instance
        if(instance == null){
            //若实例未创建,则向下执行来竞争锁
            //竞争成功的锁,进行创建实例操作
            synchronized (LazySingleton.class){
                //在竞争成功的锁创建实例并释放锁后
                //其他竞争到锁的线程被内层if挡住,不会创建多个实例
                if (instance == null){
                    instance = new LazySingleton();
                }
            }
        }
        return instance;
    }

通过两个if条件判断,降低了锁竞争的频率,既保证了线程安全,又提高了执行效率

此时线程安全了吗?

此时,指令重排序,也可能引起线程安全问题

指令重排序,是编译器优化的一种方式,通过调整原有代码的执行顺序,在保证逻辑不变的前提下,提高程序的效率

在创建实例时,可将其分为三个步骤

1. 申请一段内存空间

2. 在该内存空间上调用构造方法,创建出实例

3. 将该内存地址赋值给instance引用变量

正常情况下,创建实例是按照1 2 3的顺序来执行的,而编译器也可能会将其优化为1 3 2的顺序来执行

而当按照1 3 2 的方式来创建实例时,就可能会出现问题

如何解决指令重排序带来的线程安全问题?

使用volatile 

volatile能够禁止指令重排序,从而保证创建实例时,按照1 2 3的顺序来创建出实例,保证创建出初始化的实例

public class LazySingleton {
    //在类中创建唯一实例,并将其置为null
    //volatile:禁止指令重排序
    private volatile static LazySingleton instance = null;

    //构造方法私有化,保证类外部不能进行实例化
    private LazySingleton() {
    }

    //对外提供获取该唯一实例的方法
    public static LazySingleton getInstance() {
        //判断是否已经创建实例,若已经创建实例,则不加锁,直接返回instance
        if (instance == null) {
            //若实例未创建,则向下执行来竞争锁
            //竞争成功的锁,进行创建实例操作
            synchronized (LazySingleton.class) {
                //在竞争成功的锁创建实例并释放锁后
                //其他竞争到锁的线程被内层if挡住,不会创建多个实例
                if (instance == null) {
                    instance = new LazySingleton();
                }
            }
        }
        return instance;
    }
}

懒汉模式在类加载时,并没有进行实例化,而是在第一次调用getInstance()方法的时候,才进行实例化。若一直没有调用getInstance()方法,则不创建该唯一实例,此时节省了实例化的开销

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

KNN朴素贝叶斯(根据已知推测未知)

KNN(哲学思想:物以类聚,人以群分) KNN算法原理及示例1: 向量化 画点,计算欧式距离: 可行代码展示: #!/usr/bin/python # codingutf-8 ######################################### …

如何在 VeriStand 中设置反射内存通道

环境 硬件 cPCI-5565PIORC 软件 VeriStand 我正在设置我的反射内存 PXI 卡(例如 cPCI-5565PIORC)。 我可以在我的 PXI 系统之间使用反射内存发送/接收什么? 如何设置我的 PXI 系统之间共享的通道? 使用反射内存,您…

国际语音呼叫中心的工作流程

国际语音呼叫中心的工作流程一般包括以下几个步骤: 1.呼叫分配 当客户拨打企业的客服电话时,国际语音呼叫中心会自动将呼叫分配给示闲的客服代表,或者根据客户的需求,将呼叫转接给相应的客服代表。 2.客服代表接听电话 客服代…

网络小测------

使用软件PT7.0按照上面的拓扑结构建立网络,进行合理配置,使得所有计算机之间能够互相通信。并且修改各交换机的系统名称为:学号_编号,如你的学号为123,交换机Switch0的编号为0,则系统名称为123_0&#xff1…

史上最全的设计模式总结

从七月份开始一直到九月底才看完设计模式,在这个过程中我不敢说我已经掌握了那本书里面的内容,或者说1/5,没能力说也没有资格说。但是结果不重要,重要的是这个过程我的收获!主要包括如下几个方面: 1、认识了…

华为OD机试 - 任务最优调度 - 深度优先搜索dfs算法(Java 2023 B卷 200分)

目录 专栏导读一、题目描述二、输入描述三、输出描述1、输入2、输出3、说明 四、解题思路1、题目解读2、解题思路3、具体步骤 五、Java算法源码六、效果展示1、输入2、输出3、说明思路分析执行顺序 华为OD机试 2023B卷题库疯狂收录中,刷题点这里 专栏导读 本专栏收…

Vue3+Ts项目(Naive UI组件)——创建有图标可伸缩的左边菜单栏

文章目录 安装、配置vue-router1、安装2、main.ts配置3、在App.vue中,渲染路由配置到的组件 创建测试路径页面1、src\views\dashboard\index.vue2、src\views\dashboard\test.vue3、src\views\table\index.vue 配置页面路由1、src\router\modules\dashboard.ts2、sr…

Java-----链表

本篇碎碎念:唐朝诡事录中的西安与洛阳让我想到了,远赴人间惊鸿宴会,一睹人间盛世颜,描绘的就是这两个古都吧,有机会一定要去游览一番 今日份励志文案: 最好的状态就是向自己喜欢的东西一点点靠近 …

基于SSM的OA办公系统设计与实现

末尾获取源码 开发语言:Java Java开发工具:JDK1.8 后端框架:SSM 前端:Vue 数据库:MySQL5.7和Navicat管理工具结合 服务器:Tomcat8.5 开发软件:IDEA / Eclipse 是否Maven项目:是 目录…

在线免费压缩pdf文件

在线免费压缩pdf文件,不用登陆哦, https://www.ilovepdf.com/ https://online2pdf.com/#

IPIDEA科普大数据企业怎样使用IP代理工具进行数据抓取

相信有很多的朋友都很好奇一件事,一般大数据企业需要拥有海量的数据才能够进行数据分析整理和利用,那么他们都是如何抓取到这么多的数据呢?这些企业在抓取数据时都会使用什么工具,今天就跟大家科普一下。 其实大数据企业在进行数…

uniapp x 相比于其他的开发系统框架怎么样?

首先我们要知道niapp这是一种基于Vue.js开发的跨平台应用框架,可以将同一套代码同时运行在多个平台上,包括iOS、Android、H5等。相比其他开发系统框架,他有什么优点呢?让我们共同探讨一下吧! 图片来源:unia…

《数据结构、算法与应用C++语言描述》-最大高度优先左高树-C++实现

左高树 完整可编译运行代码见:Github::Data-Structures-Algorithms-and-Applications/_26maxHblt 定义 (大顶堆和小顶堆)堆结构是一种隐式数据结构(implicit data structure)。用完全二叉树表示的堆在数组中是隐式存储的(即没有明确的指针或其他数据能够用来重塑…

HTML5+CSS3+JS小实例:可拖拽排序的人物列表

实例:可拖拽排序的人物列表 技术栈:HTML+CSS+JS 效果: 源码: 【HTML】 <!DOCTYPE html> <html><head><meta http-equiv="content-type" content="text/html; charset=utf-8"><meta name="viewport" content=…

关东升老师极简系列丛书(由清华大学出版社出版)

极简系列丛书&#xff0c;编程学习新体验 在这个科技日新月异的时代&#xff0c;编程已经成为了一种必备技能。但是面对各种复杂的编程语言&#xff0c;你是否也曾感到过迷茫和困惑&#xff1f;由清华大学出版社出版的“极简系列丛书”就是为了帮助你解决这个问题。 这套丛书…

抖捧自动直播是什么,系统功能讲解

目前有在做实体行业级商家服务的老板 你还在为不会直播&#xff0c;不敢直播而苦恼吗&#xff1f; 你还在为想做直播&#xff0c;但没空开直播而焦灼吗&#xff1f; 今天&#xff0c;你的问题都可以统统解决 实体行业直播必备黑科技&#xff1a;抖捧AI自动直播 只需要一部手…

3号线开通在即, 你的「搭子」找好了吗?

搭子合伙者抱有同样目的的人 “搭子”作为一种新型社交关系和社交方式&#xff0c;正在年轻人当中盛行。 浅于朋友&#xff0c;重于同事&#xff0c; 主打“垂直领域”和“精准陪伴”。 不同场合大家都有专属“搭子”&#xff0c; “周末去孔学堂感受传统文化的研学搭子”“…

51单片机控制1602LCD显示屏输出两行文字一

51单片机控制1602LCD显示屏输出两行文字一 1.概述 这篇文章介绍1602型号显示屏的基础知识&#xff0c;以及使用单片机控制它输出两行内容。 2.1602基础知识 1602 液晶显示模块是一种通用的工业液晶显示模块&#xff0c;专门用来显示字母、数字、符号等的点阵型液晶显示模块…

宝塔PostgreSQL设置数据库远程访问

宝塔PostgreSQL设置数据库远程访问 宝塔的PostgreSQL1. 添加数据库2. 打开PostgreSQL设置界面3. 修改配置4. 重载配置/重启数据库 Docker的PostgreSQL1. postgresql.conf2. pg_hba.conf3. 重启数据库 注意其他问题 宝塔PostgreSQL设置数据库远程访问&#xff1f;docker容器Post…