设计模式之单列模式

news2024/12/23 20:47:47

单列模式是一种经典的设计模式,在校招中最乐意考的设计模式之一~

设计模式就是软件开发中的棋谱,大佬们针对一些常见的场景,总结出来的代码的编写套路,按照套路来写,不说你写的多好,至少不会太差~

在校招中,主要考察两个设计模式:单列模式,工厂模式

本文主要讲解单列模式

单列《——》单列模式(instance)对象

一个程序中,某个类只创建出一个实列(对象)——》不能创建多个对象!

Java中的单列模式借助Java语法,保证某个类只能创建出一个实列,而不能new多次!!有些场景,本身就是要求某个概念是单列的!!

在Java语法中,如何做出单列模式的实现呢??

Java中实现单列模式的有很多种写法:起码有5/6种写法,但是课堂上主要说两种:

  • 饿汉模式(饥饿)
  • 懒汉模式(从容)

饿汉:吃完饭就立刻去洗碗

懒汉:吃完饭之后,先把碗放到一边,等到下一顿吃的时候,需要用到碗了再洗,通常认为懒汉模式更好(效率更高)

比如:中午吃饭,用了4个碗

饿汉:吃完就得把4个碗都洗了

懒汉:晚上吃饭只用2个碗,此时只需要洗2个就ok了

计算机中的列子,打开一个硬盘上的文件,读取文件内容,并显示出来:

饿汉:把文件所有的内存都读到内存中,并显示出来

懒汉:只把文件读一小部分,把当前屏幕填充上,如果用户翻页了,在读其他文件内容,如果不翻页就省下了

假设文件非常大:10G:

饿汉模式:文件可能卡半天!内存够不够??

懒汉模式:可以快速打开!

我们来看一下下述代码吧:饿汉模式:

class Singleton{
    //唯一的实体
    private static Singleton instance=new Singleton();
    
    //获取到实列的方法
    public static Singleton getInstance(){
        return instance;//单词的读操作,不涉及修改
    }

    //禁止外部new实列
    //此处,在类内部把实列创建好,同时禁止外部重新创建实列,此时就可以保证是单列的特性了!
    private Singleton(){
        //构造方法设为private
        //仅类内部使用
    }
}


public class Main {
    public static void main(String[] args) {
        //此时s1和s2是同一个对象
        Singleton s1=Singleton.getInstance();
        Singleton s2=Singleton.getInstance();
        
        //Singleton s3=new Singleton();
        //此处要把new操作禁止掉,则把该类的构造方法设为private即可
    }
}

上述代码是线程安全的(饿汉模式),先创建好实列,随去随用~

private static Singleton instance=new Singleton();

在上述的该段代码中:被static修饰,该属性是类的熟悉,JVM中,每个类的类对象只有唯一一份,类对象里的这个成员,自然也是唯一的!!

但是对于创建的这个实列,我们可以用不到,因此:非必要不创建《——》懒汉模式

懒汉模式的核心思想:——》非必要不创建

//通过懒汉模式实现一下单列模式
class SingletonLazy{
    volatile private static SingletonLazy instance=null;//先置为空

    //只有调用getInstance才会new一个对象,如果再次调用getInstance,仍然会返回之前的实列
    public static SingletonLazy getInstance(){
        //这个条件,判定是否需要加锁,如果对象已经有了,
        //就不必加锁了,此时本身就是线程安全的
        if (instance==null){
            synchronized (SingletonLazy.class){
                //加锁,保证判定和new是一个原子操作
                if (instance==null){
                    instance=new SingletonLazy();
                }
            }
        }
        return instance;//唯一
    }

    private SingletonLazy(){
        //构造方法设为private
        //类中可以访问,类外不能访问
    }
}


public class Main1 {
    public static void main(String[] args) {
        SingletonLazy s1=SingletonLazy.getInstance();
        SingletonLazy s2=SingletonLazy.getInstance();
        //此时s1和s2是同一个实列
        System.out.println(s1==s2);//true
    }
}

显而易见,上述代码的运行结果为:

思考一下:对于前面的饿汉模式和懒汉模式的两个代码,是否线程安全??

对于饿汉模式:认为线程安全的——》只是读数据

对于懒汉模式:多线程下调用getInstance,可能会出现问题《——》线程不安全

多线程下,懒汉模式可能无法保证创建对象的唯一性!!

懒汉模式的部分代码如下:

如果是N个线程一起调用,可能就会搞出N个对象了!!

通过加锁操作《——》锁要加在哪里??多线程的代码是很复杂的,不是说只要写了加锁操作,就一定是线程安全了,只能具体问题具体分析。

加锁,是一个比较低效的操作(加锁就可能涉及到阻塞等待~)!!非必要不加锁

对于getInstance()这个方法:

在该段代码中,任何时候调用getInstance(),都会触发锁的竞争。

其实,此处的线程不安全,只出现在首次创建对象这里,一旦对象new好了,后续在调用getInstance(),就只是单纯的读操作了,就没有线程安全问题,就没有必要再加锁了!!

因此,可以在加一个if语句的判定(两个if语句的判定更加完美)

这两个if (instance==null)的代码,看起来一样,实际上,他俩的差别很大!!按照咱们之前的理解,两行代码如果紧挨着的,此时两行代码就会被迅速的执行完,近似就可以看作是“同一时机”,实际上,由于这两个if中间间隔了个synchronized,加锁可能导致阻塞,至于啥时候解除阻塞,时间不一定!!虽然两个条件相同,但是如果调用时间长了,结果也可能会不同~!!

加上volatile可以解决指令重排序的问题:

volatile private static SingletonLazy instance=null;//先置为空

小结一下,瞬间开心:

单列模式的线程安全问题:(经典面试题)

饿汉模式:天然就是安全的,只是读操作

懒汉模式:不安全,有读也有写操作

  • 加锁,把if和new变成原子操作
  • 双重if,减少不必要的加锁操作
  • 使用volatile,禁止指令重排序,保证后续线程拿到的是完整对象

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

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

相关文章

Java的环境配置

目录 window系统安装java下载JDK配置环境变量JAVA_HOME 设置PATH设置CLASSPATH 设置测试JDK是否安装成功 Linux,UNIX,Solaris,FreeBSD环境变量设置流行JAVA开发工具使用 Eclipse 运行第一个 Java 程序 window系统安装java 下载JDK 首先我们…

OLED透明屏触控:引领未来科技革命的创新力量

OLED透明屏触控技术作为一项颠覆性的创新,正在引领新一轮科技革命。它将OLED显示技术与触摸技术相结合,实现了透明度和触控功能的完美融合。 在这篇文章中,尼伽将通过引用最新的市场数据、报告和行业动态,详细介绍OLED透明屏触控…

Java知识总结(持续更新)

一、JDK、JRE、JVM三者之间的关系? 1. **JDK (Java Development Kit)**: JDK 是 Java 开发工具包,它包含了用于开发 Java 应用程序的所有必要工具和库。这包括 Java 编译器(javac)、Java 核心类库、开发工具&#x…

【Day-33慢就是快】代码随想录-二叉树-二叉搜索树中的搜索

给定二叉搜索树(BST)的根节点和一个值。 你需要在BST中找到节点值等于给定值的节点。 返回以该节点为根的子树。 如果节点不存在,则返回 NULL。 思路 什么是二叉搜索树: 二叉搜索树是一个有序树: 若它的左子树不空&a…

【NVIDIA CUDA】2023 CUDA夏令营编程模型(三)

博主未授权任何人或组织机构转载博主任何原创文章,感谢各位对原创的支持! 博主链接 本人就职于国际知名终端厂商,负责modem芯片研发。 在5G早期负责终端数据业务层、核心网相关的开发工作,目前牵头6G算力网络技术标准研究。 博客…

yolov7增加mobileone

代码地址:GitHub - apple/ml-mobileone: This repository contains the official implementation of the research paper, "An Improved One millisecond Mobile Backbone". 论文地址:https://arxiv.org/abs/2206.04040 MobileOne出自Apple&am…

IP403参考资料和引脚图

特性 支持串并联双LED驱动和GPIO控制器 串并联LED驱动器 支持一个串行输入56个并行led或三串行输入16个并行ed为每个串行输入。 支持4-16mA可调电流源驱动,无限流电阻 支持LED熄灭模式,实现省电功能 提供级联能力,提供更多的LED驱动输出…

go语言基础---8

Http请求报文格式分析 package mainimport ("fmt""net" )func main() {//监听listener, err : net.Listen("tcp", ":8000")if err ! nil {fmt.Println("listener err", err)return}defer listener.Close()//阻塞等待用户的…

SAM + YOLOv8 图像分割及对象检测

SAM(Segment Anything Model)是由 Meta 的研究人员团队创建和训练的深度学习模型。该创新成果发表在 2023 年 4 月 5 日发表的一篇研究论文中,它立即引起了公众的广泛兴趣——相关的 Twitter 帖子迄今为止已累积超过 350 万次浏览: 计算机视…

接口文档生成工具JAPiDocs

效果如下: 相比Swagger要写一堆注解,Spring RestDocs需要写测试用例,才能生成API文档。 要使得JApiDcos正确工作,代码应该是像下面的样子的: /*** 用户接口*/ RequestMapping("/api/user/") RestControll…

YOLO目标检测——口罩规范佩戴数据集+已标注xml和txt格式标签下载分享

实际项目应用:目标检测口罩佩戴检测数据集的应用场景涵盖了公共场所监控、疫情防控管理、安全管理与控制以及人员统计和分析等领域。这些应用场景可以帮助相关部门和机构更好地管理口罩佩戴情况,提高公共卫生和安全水平,保障人们的健康和安全…

C++数据结构X篇_10_C++栈的应用-中缀转后缀

中缀表达式就是我们平时运算表达式,其特点是运算符总是处于两个运算对象之间。但是该表达式计算机处理起来较为麻烦,会将其转写成后缀表达式,后缀表达式也叫逆波兰表达式,后缀表达式的特点是每个运算符都置于两个运算对象之后。此…

微信小程序AI类目-深度合成-AI问答/AI绘画 互联网信息服务算法备案审核通过教程

近期小程序审核规则变化后,很多使用人类小徐提供的chatGPT系统的会员上传小程序无法通过审核,一直提示需要增加深度合成-AI问答、深度合成-AI绘画类目,该类目需要提供互联网信息服务算法备案并上传资质,一般对企业来说这种务很难实…

SSH是如何配置的

目录 什么是SSH SSH可以做什么其他用途? ssh有几种连接方法吗 我应该用哪种方法连接SSH1或SSH2? 每天都在用SSH你知道SSH的原理吗 开启ssh后telnet会关闭吗 SSH的优缺点 SSH和Telnet之间优缺点的对比 SSH的配置实验 ensp Cisco H3C 1、什么是…

linux内核模块编译方法详解

文章目录 前言一、静态加载法1.1 编写驱动程序1.2 将新功能配置在内核中1.3为新功能代码改写Makefile1.4 make menuconfig界面里将新功能对应的那项选择为<*> 二、动态加载法2.1 新功能源码与Linux内核源码在同一目录结构下2.2 新功能源码与Linux内核源码不在同一目录结构…

0003号因子测试结果、代码和数据

这篇文章共分为四个部分:第一个部分是因子测试结果,第二个部分是因子逻辑,第三个部分是因子代码,第四个部分是整个因子测试用的数据、代码、分析结果的下载地址。 因子测试结果: 因子描述 因子属性-量价因子因子构建:计算成交量的变化率和日振幅率,计算两者在过去一定…

Solidity 小白教程:12. 事件

Solidity 小白教程&#xff1a;12. 事件 这一讲&#xff0c;我们用转账 ERC20 代币为例来介绍solidity中的事件&#xff08;event&#xff09;。 事件 Solidity中的事件&#xff08;event&#xff09;是EVM上日志的抽象&#xff0c;它具有两个特点&#xff1a; 响应&#x…

外传-Midjourney的局部重绘功能

今天在抄袭。。。啊不&#xff0c;借鉴 midjourney 官网教程的时候&#xff0c;发现多了一个 局部重绘的功能&#xff0c;意外发觉还不错&#xff0c;分享一下用法。 先给大家说一下&#xff0c;我这段时间都在学习 SD&#xff0c;局部重绘是基操&#xff0c;而 MJ 一直是次次…

react实现一个搜索部门(input + tree)

目录 react实现一个搜索部门(input tree)searchDept.jsxtreeData.js使用组件效果 react实现一个搜索部门(input tree) searchDept.jsx import React, { useState, useEffect } from "react"; import StyleDeptId from "styled-components"; import Spl…

数据链路层重点协议-以太网

以太网简介 "以太网" 不是一种具体的网络&#xff0c;而是一种技术标准&#xff1b;既包含了数据链路层的内容&#xff0c;也包含了 一些物理层的内容。例如&#xff1a;规定了网络拓扑结构&#xff0c;访问控制方式&#xff0c;传输速率等&#xff1b; 以太网数据帧…