【七】一文带你迅速掌握设计模式中的单例模式

1. 什么是设计模式

设计模式可以理解为就是一种固定套路,就好比你和对手下棋得时候,会有一些固定套路下法;而设计模式就是软件开发的棋谱~
设计模式有很多种,接下来就介绍一种校招阶段,主要考察的两种设计模式:

  • 单例模式
  • 工厂模式

2. 单例模式

所谓的单例模式就是单个实例(对象),进一步的解释就是在一个程序中,某个类,只能创建出一个实例(一个对象),不能创建多个实例(多个对象)
在Java中的单例模式,借助Java语法,保证某个类,只能够创建出一个实例,而不能new多次~~
那为什么要有这个单例模式式呢?是因为在有些场景中,本身就要求某个概念是单例的~

Java中实现单例模式有很多种写法,接下来主要介绍两种

  • 饿汉模式
  • 懒汉模式

可以举一个计算机中的例子来解释下什么是饿汉模式,什么是懒汉模式
假设我们打开一个硬盘上的文件,读取文件内容,并显示出来
饿汉模式:把文件所有内容都读到内存中,并显示
懒汉模式:只把文件读一小部分,把当前屏幕填充上,如果用户翻页了,再读其他文件内容,如果不翻页,就省下了~
这种情况下,如果文件非常大,饿汉模式就可能打开卡半天,但是懒汉模式可以快速打开~

2.1 饿汉模式(急迫)

class Singleton{
    // 唯一实例的本体
    private static Singleton instance = new Singleton();

    // 获取到实例的方法
    public static Singleton getInstance() {
        return instance;
    }

    // 禁止外部 new 实例
    private Singleton(){

    }
}
public class Demo13 {
    public static void main(String[] args) {
        // 此时 s1 和 s2 是同一个对象
        Singleton s1 = Singleton.getInstance();
        Singleton s2 = Singleton.getInstance();
        
        // Singleton s3 = new Singleton();  此处不允许 new
    }
}

上述代码就是饿汉模式!但是有个缺点,就是在类加载的时候,就完成了实例的创建,那如果后面没有用到这个实例呢? 接下来我们介绍另外一个懒汉模式~

2.2 懒汉模式(从容)

class SingletonLazy {
    private static SingletonLazy instance = null;

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

    private SingletonLazy() {
    }
}
public class Demo14 {
    public static void main(String[] args) {
        SingletonLazy s1 = SingletonLazy.getInstance();
        SingletonLazy s2 = SingletonLazy.getInstance();
        System.out.println(s1 == s2);
    }
}

上述代码,就是懒汉模式,在调用的时候,才会真正去创建实例~

2.3 多线程下不安全

我们对比下 饿汉模式 和 懒汉模式分析下哪一个在多线程下是安全的?
在这里插入图片描述
通过对比可以,分析出 懒汉模式在多线程情况下是会不安全的!下面进行详细分析,在多线程下,懒汉模式可能无法保证创建对象的唯一性~

在这里插入图片描述

2.3.1 饿汉模式(多线程版)

class SingletonLazy {
    private static SingletonLazy instance = null;

    public static SingletonLazy getInstance() {
        synchronized (SingletonLazy.class){
            if (instance == null) {
                instance = new SingletonLazy();
            }
        }
        return instance;
    }

    private SingletonLazy() {
    }
}

public class Demo14 {
    public static void main(String[] args) {
        SingletonLazy s1 = SingletonLazy.getInstance();
        SingletonLazy s2 = SingletonLazy.getInstance();
        System.out.println(s1 == s2);
    }
}

只有确保 判定 和 new 操作是原子性的操作才能保证多线程操作下的安全! 但是其实加锁是一个比较低效的操作!(加锁就可能涉及到阻塞等待),所以一般的非必要不要加锁~

2.3.2 饿汉模式(多线程改进版)

由于上述代码中,只要调用getInstance就会触发锁竞争!~其实我们只要一分析一开始懒汉模式的代码,就会发现,此处的线程不安全操作,只出现了首次创建实例的处,一旦实例创建好以后,后续调用getInstance,只是读操作!,所以可以将代码进行优化

class SingletonLazy {
    private static SingletonLazy instance = null;

    public static SingletonLazy getInstance() {
        // 这个条件,判断是否要加锁,如果对象已经有了,就不必加锁了,此时
        if (instance == null) {
            synchronized (SingletonLazy.class) {
            	// 这个条件是判断对象是否为空
                if (instance == null) {
                    instance = new SingletonLazy();
                }
            }
        }
            return instance;
        }
    private SingletonLazy() {}
    }

    public class Demo14 {
        public static void main(String[] args) {
            SingletonLazy s1 = SingletonLazy.getInstance();
            SingletonLazy s2 = SingletonLazy.getInstance();
            System.out.println(s1 == s2);
        }
    }

只要在外层,再次加一个判定条件,这样就可以减少不必要的锁竞争。

2.3.2 饿汉模式 (多线程最终版)

尽管上述代码,已经进行了优化,但是仍旧存在一个问题,指令重排序问题!
在这里插入图片描述
所以就需要加 volatile 关键字保证指令重排序问题

class SingletonLazy {
    volatile private static SingletonLazy instance = null;

    public static SingletonLazy getInstance() {
        // 这个条件,判断是否要加锁,如果对象已经有了,就不必加锁了,
        if (instance == null) {
            synchronized (SingletonLazy.class) {
                if (instance == null) {
                    instance = new SingletonLazy();
                }
            }
        }
            return instance;
        }
    private SingletonLazy() {}
    }

    public class Demo14 {
        public static void main(String[] args) {
            SingletonLazy s1 = SingletonLazy.getInstance();
            SingletonLazy s2 = SingletonLazy.getInstance();
            System.out.println(s1 == s2);
        }
    }

2.4 总结

  • 饿汉模式:天然线程安全,只是读操作
  • 懒汉模式:不安全的,有读也有写
    • 加锁,把 if 和 new 变成原子操作
    • 双重 if,减少不必要的加锁操作
    • 使用 volatile 禁止指令重排序,保证后续线程肯定能拿到的是完整对象

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

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

相关文章

centos 7安装mysql

今天在centos 7上安装mysql数据库,遇到了些问题以及解决方法,记录一下。1、下载mysql安装包wget -i -c http://dev.mysql.com/get/mysql57-community-release-el7-10.noarch.rpm出现跟上面界面这样子,说明是ok的了,接下来&#xf…

LAMP架构之zabbix监控(2):zabbix基础操作

目录 一、zabbix监控节点添加和删除 (1)手动添加 (2)自动添加 (3)按照条件批量添加 (4)使用api工具进行管理 二、针对应用的zabbix监控 一、zabbix监控节点添加和删除 实验说明&a…

yolov5 优化系列(三):修改损失函数

1.使用 Focal loss 在util/loss.py中,computeloss类用于计算损失函数 # Focal lossg h[fl_gamma] # focal loss gammaif g > 0:BCEcls, BCEobj FocalLoss(BCEcls, g), FocalLoss(BCEobj, g)其中这一段就是开启Focal loss的关键!!&…

基于opencv的边缘检测方法

1、梯度运算 用OpenCV的形态变换( 膨胀、腐蚀、开运算和闭运算)函数morphologyEx 梯度运算即膨胀结果-腐蚀结果: 【注意】对于二值图像来说,必须是前景图像为白色,背景为黑色,否则需要进行反二值化处理 …

axios介绍和使用

简介 本文主要讲解axios的概念和基本使用。 axios时目前最流行的ajax封装库之一,用于很方便地实现ajax请求的发送。 支持的功能: 从浏览器发出 XMLHttpRequests请求。从 node.js 发出 http 请求。支持 Promise API。能拦截请求和响应。能转换请求和响…

中文文献怎么查找,带你了解中文文献查找途径及方法

在我们撰写论文和科研工作时经常会查找文献资料,今天带大家了解中文文献查找途径及方法。 查找中文文献常用网站有: 文献党下载器(wxdown.org):是一个几乎整合了所有中外文献数据库资源的文献下载平台,因为资源最多&a…

手机(Android)刷NetHunter安装指南,无需ssh执行kali命令, NetHunter支持的无线网卡列表!

一、安装NetHunter 前提:确保手机已经root,已装上magisk。如果没有root,可用尝试magisk root 后执行此文 1、下载Nethunter:Get Kali | Kali Linux 然后push 到sdcard 里, 2、打开magisk,选择刚刚下好的…

【Python学习笔记】b站@同济子豪兄 用pytorch搭建全连接神经网络,对Fashion-MNIST数据集中的时尚物品进行分类

【Python学习笔记】原作b站同济子豪兄 用pytorch搭建全连接神经网络,对Fashion-MNIST数据集中的时尚物品进行分类 跟着b站同济子豪兄的视频自学写的代码,内容是用pytorch搭建全连接神经网络,对Fashion-MNIST数据集中的时尚物品进行分类 视频…

Spring整体架构包含哪些组件?

Spring是一个轻量级java开源框架。Spring是为了解决企业应用开发的复杂性而创建的,它使用基本的JavaBean来完成以前只可能由EJB完成的事情。 Spring的用途不仅限于服务器端的开发,从简单性、可测试性和松耦合的角度而言,任何java应用都可以从…

原神 Android 教程 —安卓版

准备材料 一台能读写 /system 分区的 Android 手机(或:一台安装了 Magisk 的 Android 手机) 有人搞出来免root端了,此条件不再必须私服客户端

小米应用商店上架app隐私不合规自查整改办法

目前各大应用商店都上线了上架app隐私合规检测机制,以小米应用商店为例,只有符合法律法规及应用隐私合规上架标准要求的app才能顺利上架并展示给用户下载使用。已上架app在巡检中如果发现不满足应用隐私合规要求的,也会被下架处理。app隐私不…

ActiveMQ(三)

协议配置 ActiveMQ 支持的协议有 TCP 、 UDP、NIO、SSL、HTTP(S) 、VM 这是activemq 的activemq.xml 中配置文件设置协议的地方 <transportConnector name"openwire" uri"tcp://0.0.0.0:61616?maximumCon nections1000&amp;wireFormat.maxFrameSiz…

利用摄影测量进行地形建模的介绍

一、前言 从一个地方到另一个地方的地球表面由连续和突然的海拔变化组成&#xff0c;个人和社会都必须应对这些变化。 水从高山和丘陵向下流&#xff0c;从溪流流入河流&#xff0c;形成三角洲&#xff0c;最终汇入大海。 三维 (3D) 地面信息的获取和表示一直是与行星表面相关的…

RK3568平台开发系列讲解(调试篇)Linux 内核的日志打印

🚀返回专栏总目录 文章目录 一、dmseg 命令二、查看 kmsg 文件三、调整内核打印等级沉淀、分享、成长,让自己和他人都能有所收获!😄 📢本篇将 Linux 内核的日志打印进行梳理。 一、dmseg 命令 在终端使用 dmseg 命令可以获取内核打印信息,该命令的具体使用方法如下所…

Downie 4 4.6.12 MAC上最好的一款视频下载工具

Downie for Mac 简介 Downie是Mac下一个简单的下载管理器&#xff0c;可以让您快速将不同的视频网站上的视频下载并保存到电脑磁盘里然后使用您的默认媒体播放器观看它们。 Downie 4 Downie 4 for Mac Downie 4 for Mac软件特点 支持许多站点 -当前支持1000多个不同的站点&…

叮咚,您有一封告白信件待查收(原生HTML+CSS+JS绘制表白信件,代码+链接+步骤详解)

马上就要5月20号啦&#xff0c;准备好如何向心仪的她/他表白了嘛&#xff01;特此出一篇告白小信件&#xff0c;效果图如下。纯htmlcss绘制&#xff0c;包含详细教程注释&#xff0c;干货满满哦。 链接置于文章结尾总结处。 文章目录一、叮咚&#xff01;查收您的信件&#x…

Spring Cloud Alibaba全家桶(七)——Sentinel控制台规则配置

前言 本文小新为大家带来 Sentinel控制台规则配置 相关知识&#xff0c;具体内容包括流控规则&#xff08;包括&#xff1a;QPS流控规则&#xff0c;并发线程数流控规则&#xff09;&#xff0c;BlockException统一异常处理&#xff0c;流控模式&#xff08;包括&#xff1a;直…

thinkphp内核开源商城APP小程序H5开源源码讲解

系统功能介绍 支持点餐、桌码点餐 知识付费、家政功能 公众号管理 设置自定义菜单、被关注回复、关键字回复&#xff0c;查看公众号粉丝、素材管理、素材群发、模板消息群发、活跃粉丝群发等功能 用户领卡后在微信卡包中展示&#xff0c;实现会员卡买单消费等功能&#xff0c;…

Python实战,爬取金融期货数据

大家好&#xff0c;我是毕加锁。 今天给大家带来的是 Python实战&#xff0c;爬取金融期货数据 文末送书&#xff01; 文末送书&#xff01; 文末送书&#xff01; 任务简介 首先&#xff0c;客户原需求是获取https://hq.smm.cn/copper网站上的价格数据(注&#xff1a;获取的是…

【LeetCode】剑指 Offer 39. 数组中出现次数超过一半的数字 p205 -- Java Version

题目链接&#xff1a;https://leetcode.cn/problems/shu-zu-zhong-chu-xian-ci-shu-chao-guo-yi-ban-de-shu-zi-lcof/ 1. 题目介绍&#xff08;39. 数组中出现次数超过一半的数字&#xff09; 数组中有一个数字出现的次数超过数组长度的一半&#xff0c;请找出这个数字。 你可…