<JavaEE> 经典设计模式之 -- 单例模式(“饿汉模式”和“懒汉模式”实现单例模式)

目录

一、单例模式概述

二、“饿汉模式”实现单例模式

三、“懒汉模式”实现单例模式

3.1 单线程下的“懒汉模式”

3.2 多线程下的“懒汉模式”


一、单例模式概述

1)什么是单例模式?

单例模式是一种设计模式。

单例模式可以保证某个类在程序中只存在唯一实例,即不允许创建多份实例。

使用单例模式,上述要求就得到了检查和校验。

2)单例模式的实现形式

单例模式可以通过很多种方法实现,“饿汉模式”和“懒汉模式”是其中最基础的两种,本文只介绍这两种实现。

二、“饿汉模式”实现单例模式

通过代码演示“饿汉模式”实现的单例模式:

class Singleton{
    //新建一个唯一实例;
    private static Singleton instance = new Singleton();

    //方法返回唯一实例;
    public static Singleton getInstance() {
        return instance;
    }

    //将构造方法私有化;
    private Singleton() { }
}
1)上述代码做了什么?

创建了一个被 static 修饰的实例,这个实例成为了类属性。类对象只会有一个,这个类属性也只会有一个。

私有化构造方法,外部无法 new 新的实例,只能通过 get 方法获取唯一的那一个 instance。
2)为什么叫做“饿汉模式”?

上述代码中,实例是类属性。类属性在类加载的时候就创建了,创建时机早,十分“迫切”,因此称为“饿汉模式”。

代码证明“饿汉模式”返回的实例是唯一的:

public class Singleton_Demo0 {
    public static void main(String[] args) {
        //想直接new对象,就会报错;
        //Singleton instance = new Singleton();
        
        //两次调用getInstance()方法并分别赋值;
        Singleton instance1 = Singleton.getInstance();
        Singleton instance2 = Singleton.getInstance();

        //对比两个变量,发现是同一实例;
        if(instance1 == instance2){
            System.out.println("两个对象是同一个对象");
        }
    }
}

//运行结果:
两个对象是同一个实例
3)“饿汉模式”的单例模式在多线程下是线程安全的吗?

上述代码中,get 方法返回的是已经创建好的实例,这个操作本质上只是一个“读操作”,多个线程读取同一个变量并不会造成线程不安全。

因此“饿汉模式”的单例模式在多线程下是线程安全的。

三、“懒汉模式”实现单例模式

3.1 单线程下的“懒汉模式”

通过代码演示懒汉模式现的单例模式:

class Singleton{
    //声明一个变量作为类属性;
    private static Singleton instance = null;

    //判断变量是否为null,是则创建实例后返回,否则返回;
    public static Singleton getInstance() {
        if(instance == null){
            instance = new Singleton();
        }
        return instance;
    }

    //将构造方法私有化;
    private Singleton() { }
}
1)上述代码做了什么?

声明了一个类属性。类对象只会有一个,这个类属性也只会有一个。

私有化构造方法,外部无法 new 新的实例,只能通过 get 方法获取唯一的那一个 instance。
get 方法中根据变量是否为 null 判断是否应该创建实例。
2)为什么叫做“懒汉模式”?

上述代码中,实例是在程序员第一次调用 get 方法后才创建的,创建时机较晚,或者根本不用创建,因此称为“懒汉模式”。

3.2 多线程下的“懒汉模式”

1)单线程下的“懒汉模式”在多线程下是线程安全的吗?

答案是否定的,单线程下的“懒汉模式”在多线程下是线程不安全的,我们可以从以下两个方面分析:

“原子性”:

上述代码中判断变量是否为空的代码 —— if(instance == null),和实例化代码 —— 

instance = new Singleton(),并非是“原子”的。在多线程环境下,这就可能导致线程不安全。

可以使用 synchronized 关键字,将这两句代码加锁,解决这个问题。

内存可见性和指令重排序:

因为 instance 是一个被 static 修饰的共享数据,而且编译器内部可能对实例化的代码 —— new Singleton(),进行了编译器优化。

这就无法保证内存的可见性和指令的顺序执行,因此在多线程环境下可能导致线程不安全。

可以使用 volatile 关键字,对共享数据 instance 进行修饰,解决这个问题。

使用以上两个关键字的原因和方式,详细请参考以下博客:

阅读指针 -> 《synchronized 关键字 和 volatile 关键字》<JavaEE> synchronized关键字和锁机制 -- 锁的特点、锁的使用、锁竞争和死锁、死锁的解决方法-CSDN博客文章浏览阅读70次。介绍了 synchronized 关键字 和 锁机制,其中重点介绍了锁的特点、使用方法和死锁的相关内容。https://blog.csdn.net/zzy734437202/article/details/134742168<JavaEE> volatile关键字 -- 保证内存可见性、禁止指令重排序-CSDN博客文章浏览阅读59次。简单介绍什么是内存可见性和指令重排序。volatile关键字可以将这两种编译器优化强制关闭。https://blog.csdn.net/zzy734437202/article/details/134757070

2)“懒汉模式”在多线程下应该怎么编写?

根据上述分析,根据单线程模式下的“懒汉模式”进行改进。

方法如下:

增加 volatile 关键字对共享数据进行修饰。

为判断是否为 null 和 实例化的代码加锁,使这两句代码称为“原子”。

增加 volatile 关键字对共享数据进行修饰:

private volatile static Singleton instance = null;

为判断是否为 null 和 实例化的代码加锁,使这两句代码称为“原子”:

    public static Singleton getInstance() {
        synchronized (locker){
            if(instance == null){
                instance = new Singleton();
            }
        }
        return instance;
    }
3)“双重校验锁”

我们再仔细分析一下上述的 get 方法。

假设程序需要多次调用这个 get 方法,那么每一次进入都会进行加锁,加锁是会增加系统开销的。

那么是否真的有必要每次都加锁呢?

当 get 方法被第一次调用,实例就会被创建,那么后续再调用这个 get 方法时,返回实例就好了,加锁部分的代码块,完全可以不用执行。

在加锁的代码块之外,再增加一个if(instance == null)进行判断,那么实例在被创建之后,也就不会再进入加锁的代码块中了。

我们成功利用“双重校验锁”,优化了程序。

代码演示“双重校验锁”优化后的 get 方法:

    public static Singleton getInstance() {
        //这个if用于判断是否需要加锁;
        if(instance == null){
            synchronized (locker){
                //这个if用于判断是否需要新建实例;
                if(instance == null){
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }

经过以上的完善和优化,我们终于可以写出在多线程下保证线程安全的“懒汉模式”单例模式了:

class Singleton{
    //声明一个变量作为类属性;
    private volatile static Singleton instance = null;

    private static final Object locker = new Object();

    //判断变量是否为null,是则创建实例后返回,否则返回;
    public static Singleton getInstance() {
        //这个if用于判断是否需要加锁;
        if(instance == null){
            synchronized (locker){
                //这个if用于判断是否需要新建实例;
                if(instance == null){
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }

    //将构造方法私有化;
    private Singleton() { }
}

阅读指针 -> 《经典设计模式之 -- 使用阻塞队列实现“生产者-消费者模型”》

<JavaEE> 经典设计模式之 -- 使用阻塞队列实现“生产者-消费者模型”-CSDN博客自己实现了的阻塞队列,介绍了经典的设计模式“生产者-消费者模型”。https://blog.csdn.net/zzy734437202/article/details/134807241

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

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

相关文章

Leetcode 40 组合总和 II

题意理解: 每个数字在每个组合中只能使用 一次 数字可以重复——>难点(如何去重) 每个组合和target 求组合,对合限制,考虑回溯的方法。——将其抽象为树结构。 树的宽度——分支大小 树的深度——最…

分配栈空间的三种方式(基于适配qemu的FreeRTOS分析)

1、定义全局的数组 定义的全局数组属于bss段,相当于把bss段的一部分作为栈空间,栈空间的大小就是数组的大小如果把栈空间放在bss段,则在bss段清零时会多清零一段地址空间 2、在链接脚本中指定 用链接脚本在所有段的后面增加stack段&#xff…

Altair Radioss碰撞 安全与冲击 衡祖仿真

Altair Radioss是解决瞬态加载工况下非线性问题的领先的结构分析求解器。其具备高扩展性、高品质、高鲁棒性,以及诸多功能:多域求解技术、高级材料功能(复合材料)等。Radioss求解器被广泛应用于汽车、航空航天、电子/家电、包装、轨道机车、生物医疗、能…

数据结构(C语言)

链表 链表的基本能操作 #include <stdbool.h> #include <stdio.h> #include <stdlib.h>//链表的接口 typedef struct node_s{int val;struct node_s*next; } Node; typedef struct linkedlist_s{Node* head;Node* tail;int size; }LinkedList;//创建空链表…

腾讯物联网平台之规则引擎

1.腾讯物联网平台简介 腾讯云物联网开发平台&#xff08;IoT Explorer&#xff09;为客户提供便捷的物联网开发工具与服务&#xff0c;助力客户更高效的完成设备接入&#xff0c;并为客户提供物联网应用开发及场景服务能力&#xff0c;帮助客户高效、低成本构建物联网应用。  …

Java LeetCode篇-二叉树经典解法(实现:判断平衡二叉树、找两个节点最近的祖先等)

&#x1f525;博客主页&#xff1a; 【小扳_-CSDN博客】 ❤感谢大家点赞&#x1f44d;收藏⭐评论✍ 文章目录 1.0 平衡二叉树 1.1 实现判断平衡二叉树的思路 1.2 代码实现判断平衡二叉树 2.0 二叉树的层序遍历 2.1 实现二叉树层序遍历的思路 2.2 代码实现二叉树层序遍历 3.0 …

Linux开发工具--vim

Linux开发工具--vim 一、vim的基本概念二、常见命令三、简单配置vim配置文件的位置常用配置选项&#xff0c;用来测试使用插件 一、vim的基本概念 vim编辑器&#xff0c;只负责写代码&#xff0c;vim是一款多模式的编辑器 vim的三种模式(其实有好多模式&#xff0c;目前掌握这…

PbootCMS 前台RCE漏洞复现

0x01 产品简介 PbootCMS是全新内核且永久开源免费的PHP企业网站开发建设管理系统,是一套高效、简洁、 强悍的可免费商用的PHP CMS源码,能够满足各类企业网站开发建设的需要 0x02 漏洞概述 PbootCMS v<=3.1.6版本中存在模板注入,攻击者可构造特定的链接利用该漏洞,执行…

跟着我学Python基础篇:06.列表

往期文章 跟着我学Python基础篇&#xff1a;01.初露端倪 跟着我学Python基础篇&#xff1a;02.数字与字符串编程 跟着我学Python基础篇&#xff1a;03.选择结构 跟着我学Python基础篇&#xff1a;04.循环 跟着我学Python基础篇&#xff1a;05.函数 目录 往期文章1. 列表的基本…

ES-分析器

分析器 两种常用的英语分析器 1 测试工具 #可以通过这个来测试分析器 实际生产环境中我们肯定是配置在索引中来工作 GET _analyze {"text": "My Moms Son is an excellent teacher","analyzer": "english" }2 实际效果 比如我们有下…

win10脚本 | 使用 Word 自动化对象模型找出指定路径下含有特定内容的.docx

场景 今年的实验日志被我放在这样一个文件夹下&#xff0c;每个月下是每天具体的.docx文件&#xff0c;里面记录了我的一些实验操作步骤。现在我需要补充一个实验&#xff0c;用到一个名为chatunitest的插件&#xff0c;但是这是很久之前做的事情了&#xff0c;我无法判断是哪…

PHP 之道(PHP The Right Way 中文版)

PHP 之道&#xff08;PHP The Right Way 中文版&#xff09;

2022年重庆市职业院校技能大赛高职组“信息安全管理与评估”赛项竞赛任务书-试题01

信息安全管理与评估 第一阶段 网络平台搭建与设备安全防护 目 录 第一阶段竞赛项目试题 介绍 所需的设备、机械、装置和材料 评分方案 注意事项 项目和任务描述 1.网络拓扑图 2.IP地址规划表 工作任务 任务1&#xff1a;网络平台搭建 任务2&#xff1a;网络安全设备…

Find My手链|苹果Find My技术与手链结合,智能防丢,全球定位

手链是一种首饰&#xff0c;配戴在手腕部位&#xff0c;多为金银等金属制品&#xff0c;也有矿石、水晶等制的。手链是链状的&#xff0c;以祈求平安&#xff0c;镇定心志和美观为主要用途。手链可以展示个人的风格和品味&#xff0c;通过选择不同材质、款式和颜色的手链&#…

这样的性能测试面试题,测试开发来了都不见得会把?

14.1 性能测试怎么测试 性能测试其实就是通过自动化工具模拟多种正常、峰值以及异常负载来对系统的各项性能指标进 行测试。负载测试和压力测试都属于性能测试&#xff0c;二者可结合使用。 性能指标主要有平均响应时间、90%响应时间、吞吐量、吞吐率&#xff0c;每秒事务数&am…

新版Spring Security6.2架构 (三) - Authorization

前言 书接上文&#xff0c;在经过了authentication后就是authorization了&#xff0c;本文还是对官网文档authorization的一个架构翻译和个人理解&#xff0c;后续的博客在写具体使用例子&#xff0c;从数据中认证&#xff0c;融合authentication和authorization的概念。 Aut…

【论文解读】Accelerating motion estimation by genetic algorithm approach in x265

时间&#xff1a;2018 级别&#xff1a;SCI 机构&#xff1a;College of Engineering Pune 摘要&#xff1a; 在过去 20 年&#xff0c;在视频编码和压缩领域&#xff0c;研究人员提出了几种减少运动估计的计算量和时间的技术&#xff0c;本文提出了一种基于遗传算法初始种群确…

电脑ffmpeg.dll丢失如何修复?3个详细修复的教程分享

在计算机使用过程中&#xff0c;我们经常会遇到一些错误提示&#xff0c;其中之一就是“ffmpeg.dll丢失”。ffmpeg.dll是FFmpeg多媒体框架中的一个重要组件&#xff0c;它负责处理音频和视频的编解码。当这个文件丢失或损坏时&#xff0c;可能会导致一些应用程序无法正常运行。…

2023年【G2电站锅炉司炉】报名考试及G2电站锅炉司炉考试资料

题库来源&#xff1a;安全生产模拟考试一点通公众号小程序 G2电站锅炉司炉报名考试根据新G2电站锅炉司炉考试大纲要求&#xff0c;安全生产模拟考试一点通将G2电站锅炉司炉模拟考试试题进行汇编&#xff0c;组成一套G2电站锅炉司炉全真模拟考试试题&#xff0c;学员可通过G2电…

如何有效利用餐厅预约小程序推广餐厅品牌

随着餐饮行业竞争的加剧&#xff0c;餐厅订座预约成为了吸引顾客的一种重要方式。而微信小程序作为移动互联网的重要入口之一&#xff0c;为餐厅提供了一个方便快捷的预约平台。本文将介绍如何使用乔拓云平台等第三方小程序制作平台来开发餐厅订座预约微信小程序。 首先&#x…