单例模式-图文详解

news2024/11/22 10:13:59

概念

全世界就只要一个---在整个java程序中,只有这个类的一个实例

比如Student a = new Student(); 就是Student类只创建这一个实例,只能有这一个对象存在

主要解决:一个全局使用的类频繁地创建与销毁。在内存里只有一个实例,减少了内存的开销,尤其是频繁的创建和销毁实例(比如管理学院首页页面缓存)

缺点

没有接口,不能继承,与单一 职责原则冲突,一 个类应该只关心内部逻辑, 而不关心外面怎么样来实例化。

特点

单例模式有以下特点:

(1)单例类只能有一个实例;

(2)单例类必须自己创建自己的唯一实例;(例如 反射创建的实例就会破坏单例模式)

(3)单例类必须给所有其他对象提供这一实例。

关键代码

构造函数是私有的。

1、为了保证只有一个对象,不能new对象,所以设置构造方法私有。

2、只能通过方法或者属性获取对象,如果通过属性获取,这个属性是可以修改的,所以属性只能

是私有的。所以只能通过方法获取。

3、由于我们不能new对象,所以获取对象的方法定是静态的。属性也得是静态的,因为不是静态的,静态的方法访问不到

使用场景

· 1、要求生产唯一序列号。

· 2、WEB 中的计数器,不用每次刷新都在数据库里加一次,用单例先缓存起来。

· 3、创建的一个对象需要消耗的资源过多,比如 I/O 与数据库的连接等。

注意事项:getInstance() 方法中需要使用同步锁 synchronized (Singleton.class) 防止多线程同时进入造成 instance 被多次实例化。

枚举:对象固定,私有构造器

何时使用

当您想控制实例数目,节省系统资源的时候。

在计算机系统中,线程池、缓存、日志对象、对话框、打印机、显卡的驱动程序对象常被设计成单例,这些应用都或多或少具有资源管理器的功能。每台计算机可以有若干个打印机,但只能有一个Printer Spooler,以避免两个打印作业同时输出到打印机中。每台计算机可以有若干通信端口,系统应当集中管理这些通信端口,以避免一个通信端口同时被两个请求同时调用。总之,选择单例模式就是为了避免不一致状态,避免政出多头。

实现方式

单例模式的写法有好几种,主要有三种:懒汉式单例、饿汉式单例、登记式单例。

饿汉式和懒汉式区别:

(1)初始化时机与首次调用:

  • 饿汉式是在类加载时,就将单例初始化完成,保证获取实例的时候,单例是已经存在的了。所以在第一次调用时速度也会更快,因为其资源已经初始化完成。
  • 懒汉式会延迟加载,只有在首次调用时才会实例化单例,如果初始化所需要的工作比较多,那么首次访问性能上会有些延迟,不过之后就和饿汉式一样了。

(2)线程安全方面:饿汉式天生就是线程安全的,可以直接用于多线程而不会出现问题,懒汉式本身是非线程安全的,需要通过额外的机制保证线程安全

线程安全

访问速度

性能

饿汉式

安全

懒汉式

不安全

1.饿汉式

特点

  1. 在类加载后就提前创建好了唯一实例, 并不是用到时才创建,而是提前创建
  2. 因为 唯一实例是在静态代码块里面,静态代码的执行处于类生命周期中的初始化阶段,由虚拟机保证其原子且安全执行。所以不用考虑这里的线程不安全问题,所以,饿汉式 线程安全。
  3. 就是占内存,有性能损耗
public class Singleton1 implements Serializable {

    //1.私有的构造器:构造器不是私有的话,其他类会调用你的构造器,来创建实例对象,那就有可能有多个实例了,就不是单例模式了
    private Singleton1() {
        //防止 反射强制获取到私有构造器 创建了实例对象 破坏了单例模式
        if(INSTANCE != null){
            throw new RuntimeException("单例对象不能重复创建");
        }
    }
    //2.静态的成员变量:用私有构造器创建出的唯一实例
    private static final Singleton1 INSTANCE = new Singleton1();

    //3.公共的静态方法:获得实例对象
    public static Singleton1 getInstance() {
        return INSTANCE;
    }
}

反射会破坏掉单例模式

虽然你的构造方法是私有的,但是反射可以强制获取私有的构造方法,然后再用构造方法创建实例,这样实例就不是唯一的了,单例模式就被破坏了

解决方法

在构造器中加一个判断

        //防止 反射强制获取到私有构造器 创建了实例对象 破坏了单例模式
        if(INSTANCE != null){
            throw new RuntimeException("单例对象不能重复创建");
        }

 

2.懒汉式

需要的时候才会去创建对象

  1. 好处节省内存
  2. 坏处用的时候才创建稍微有点慢

例如下面代码,就是在调用getInstance()方法时才会创建

public class Singleton2 {
    //1.私有的构造器:构造器不是私有的话,其他类会调用你的构造器,来创建实例对象,那就有可能有多个实例了,就不是单例模式了
    private Singleton2() {}
    //2.静态的成员变量:用私有构造器创建出的唯一实例
    //懒汉式 先设置null,调用的时候再创建
    private static Singleton2 single=null;
    
    //3.公共的静态方法:获得实例对象
    public static synchronized Singleton2 getInstance() {
        if (single == null) {
            single = new Singleton2();
        }
        return single;
    }
}

线程不安全

当创建了100个线程,调用getInstance方法时,可能会有多个线程同时看到没有new,就会执行多次new,调用多次构造方法(也就是会进行多次初始化)

解决方法

线程安全 给getInstance方法加synchronize锁

3.双重校验锁DCL--安全懒汉式

之前的 懒汉式单例中,为了解决线程不安全的问题,我们选择加锁,在方法调用上加了同步,虽然线程安全了,但是每次都要同步,会影响性能,毕竟99%的情况下是不需要同步的。

我们可以选择不给getInstance方法加synchronize锁,而是在这个方法里面去进行加synchronize锁,因为方法锁的范围太广,其他线程阻塞的范围就大,时间就长。

并且,我们加两个锁,在synchronized块外面加一个,里面加一个

之前的方式:锁加在方法上

    public static synchronized Singleton2 getInstance() {
        if (single == null) {
            single = new Singleton2();
        }
        return single;
    }

现在的方式:锁加在方法内部

public class Singleton3 {
    //1.私有的构造器:构造器不是私有的话,其他类会调用你的构造器,来创建实例对象,那就有可能有多个实例了,就不是单例模式了
    private Singleton3() {}
    //2.静态的成员变量:用私有构造器创建出的唯一实例
    private static volatile Singleton3 single=null;
    
    //3.公共的静态方法:获得实例对象    
    public static  Singleton3 getInstance() {
        if (single == null) {
            synchronized (Singleton3.class){
                if (single == null){
                    single = new Singleton3();
                }
            }
        }
        return single;
    }
}

为什么需要使用两个 if 进行判断呢?

我们加了两个锁,在synchronized块外面加一个,里面加一个

假设高并发下,线程A、B 都通过了第一个 if 条件。若A先抢到锁,new 了一个对象,释放锁,然后线程B再抢到锁,此时如果不做第二个 if 判断,B线程将会再 new 一个对象。使用两个 if 判断,确保了只有第一次调用单例的时候才会做同步,这样也是线程安全的,同时避免了每次都同步的性能损耗。

为什么两个if判断null:

场景:有可能多个线程同时进入了第一个if,都读到对象null了,如果第一个线程加上锁创建了对象之后,释放锁之后,如果不进行再次判断null的话,就会再次进行创建对象(以第一个判断null为准),两次就是为了防止多次创建对象

第一个if:对象为null的时候才进入下面if进行创建对象,如果不为null就return

第二个if:判断在第一个为null的多线程情况下阻止多创建对象的。

示例:如果只有一个if,也就是上面的那个

 

 

 

为什么要加 volatile?

        volatile 解决共享变量的可见性问题、有序性问题,在这里主要是解决有序性问题

        原因:可能导致对象还没创建成功(只分配了空间地址,没有数据)就返回句柄,空指针异常)、static(不必创建对象)

        加了volatile,会使在赋值语句之后加上一个内存屏障,阻止之前的一些操作越过屏障,可以阻止代码或者说指令的重排序

        volatile 的作用主要是禁止指定重排序。假设在不使用 volatile 的情况下,两个线程A、B,都是第一次调用该单例方法,线程A先执行 singleton = new Singleton(),但由于构造方法不是一个原子操作,编译后会生成多条字节码指令,由于 JAVA的 指令重排序,可能会先执行 singleton 的赋值操作,该操作实际只是在内存中开辟一片存储对象的区域后直接返回内存的引用,之后 singleton 便不为空了,但是实际的初始化操作却还没有执行。如果此时线程B进入,就会拿到一个不为空的但是没有完成初始化的singleton 对象,所以需要加入volatile关键字,禁止指令重排序优化,从而安全的实现单例。

4.静态内部类懒汉式

两全其美的方式:既有 饿汉式 线程安全 的特点,又有 懒汉式 没有性能损耗的 特点

只要方法静态代码块里面,线程就是安全的,所以我们想办法将创建实例的操作放到静态代码块里面

内部类的特点

  1. 内部类可以访问外部类的私有变量和方法
  2. 内部类 是在静态代码块里面的,态代码的执行处于类生命周期中的初始化阶段,由虚拟机保证其原子且安全执行,也就是有饿汉式的优点:线程安全
  3. 并且内部类,你不用的时候不会加载,所以也有懒汉式的优点:性能高

利用了类加载机制来保证初始化 instance 时只有一个线程,所以也是线程安全的,同时没有性能损耗,这种比上面的方法都好一些,既实现了线程安全,又避免了同步带来的性能影响。

public class Singleton {  
    //静态内部类
    private static class LazyHolder {  
       private static final Singleton INSTANCE = new Singleton();  
    }  
    private Singleton (){}  
    public static final Singleton getInstance() {  
       return LazyHolder.INSTANCE;  
    }  
}  

JDK中的单例模式

这里参考CSDN博主「哪 吒」的原创文章

原文链接:【源码分析设计模式 1】JDK中的单例模式_jdk中单例模式_哪 吒的博客-CSDN博客

1、Runtime

Runtime类封装了Java运行时环境。每一个Java程序实际上都启动了一个JVM进程,那么每个JVM进程都对应一个Runtime实例,此实例由JVM为其实例化。每个Java应用程序都有一个Runtime实例,使应用程序能够与其运行的环境相连接。

由于Java是单进程的,所以,在一个JVM中,Runtime的实例应该只有一个。所以应该使用单例来实现。

2、java.awt.Toolkit

懒汉式单例。不需要事先创建好,只要在第一次真正用到的时候再创建就可以了。因为很多时候并不常用Java的GUI和其中的对象。如果使用饿汉单例的话会影响JVM的启动速度。

 

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

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

相关文章

【白话机器学习系列】白话张量

白话张量 张量(Tensor)是向量和矩阵向 n n n 维的推广。向量是一维张量,矩阵是二维张量。张量作为数值容器,是机器学习,尤其是深度学习中最基础的操作对象,以至于 Google 的机器学习框架都已 TensorFlow …

ffmpeg在windows环境下的详细安装教程

这两天整理好用的录屏软件,发现了Captura这个软件,软件本身的安装很简单,但由于Captura需要依赖ffmpeg(一套可以用来记录、转换数字音频、视频,并能将其转化为流的开源计算机程序),而ffmpeg在安…

springboot X-Accel-Redirect 大文件下载实现

文章目录 前言一、什么是 X-Sendfile?二、相关请求头说明三、食用步骤总结 前言 文件下载的方式: nginx代理附件路径,直接访问。无法控制用户的权限。服务端流式读取文件内容。这个过程需要后端进程将文件读取到内存中然后再发给用户&#…

全民开发|零代码平台搭建库存管理系统,助力企业降本增效

什么是库存管理系统 库存管理系统是一种用于监控和跟踪商业组织存货的系统机软件。它帮助企业管理其库存,确保所需的商品和服务始终可用,并在需要时提供报告和预测。库存管理系统可追踪库存级别、库存周转率、订单、销售和交付等方面的数据,…

LIS系统源码

LIS系统(Laboratory Information System) 即实验室(检验科)信息系统,它是医院信息管理的重要组成部分之一,自从人类社会进入信息时代,信息技术的迅速发展加快了各行各业现代化与信息化的进程。LIS系统逐步采用了智能辅…

​​​​Linux Shell 实现一键部署postgres15

postgres 前言 PostgreSQL 是一个功能强大的开源对象关系数据库系统,拥有超过 35 年的积极开发经验 这为其赢得了可靠性、功能稳健性和性能的良好声誉。 通过官方文档可以找到大量描述如何安装和使用 PostgreSQL 的信息。 开源社区提供了许多有用的地方来熟悉Postg…

【是德出品,必属精品】示波器探头的11个误解

误解1. 100 MHz 的“信号”,使用 100 MHz 的示波器探头。 示波器探头带宽与配合它们使用的示波器带宽采用相同的方法进行规定,即产品响应的 -3dB 点。举例来说,如果使用 100 MHz 带宽的探头测量 100 MHz 1Vpp 正弦波,那么探头输出…

挖掘算力产业的创新力量和新型机遇|2023 开放原子全球开源峰会先进计算分论坛即将启幕

随着高新技术激发出磅礴的发展势能,海量的算力需求也在不断提升。如何升级和创新现有算力格局,打造多维感知、异构计算、智能调度的先进计算算力网?如何真正实现 “打破算力烟囱”、让算力像水和电一样流动起来?已成为时下焦点话题…

提升倾斜摄影三维模型数据的几何坐标纠正和三维重建速度具体技术方法探讨

提升倾斜摄影三维模型数据的几何坐标纠正和三维重建速度具体技术方法探讨 倾斜摄影三维模型数据的几何坐标变换和三维重建是一项非常耗时的任务,因此如何提高其速度一直是研究的重点。以下是一些实现倾斜摄影三维模型数据的几何坐标变换和三维重建速度的方法&#x…

网站建设对企业会有什么影响

随着互联网的发展,如今网络的普及,很多企业都开始搭建网站,以便展示自己的企业形象和产品。很多企业会认为,网站建设是一件很麻烦的事情,如果需要在网上宣传的话,就用一个链接来做好了。实际上不是这样的&a…

Linux 系统大技能,搞定 90% 日常运维

一、Linux 系统日常运维九大技能 1、安装部署 方式:U盘,光盘和网络安装 其中网络安装已经成为了目前批量部署的首选方式:主要工具有Cobbler和PXEkickstart 可以参考如下链接内容: http://www.cnblogs.com/mchina/p/centos-px…

智能语音信息处理团队14篇论文被语音技术顶会Interspeech 2023接收

近日,Interspeech 2023会议发出了审稿结果通知,语音及语言信息处理国家工程研究中心智能语音信息处理团队共14篇论文被会议接收,论文方向涵盖语音识别、语音合成、话者识别、语音增强、情感识别、声音事件检测等,各接收论文简介见…

WordPress主题Modown_v8.7主题免授权+Erphpdown15.21+团购+第三方登录

团购内置在主题里面了已不需要安装插件了,官方带免费子主题,主题无需授权和其他操作,安装启用即可免授权使用 主题简介 Modown是模板兔基于Erphpdown wordpress下载插件开发的一款全新的针对收费付费下载资源/付费查看内容/VIP会员免费下载查看/虚拟资源售卖的WordPress主题…

基于html+css的图展示104

准备项目 项目开发工具 Visual Studio Code 1.44.2 版本: 1.44.2 提交: ff915844119ce9485abfe8aa9076ec76b5300ddd 日期: 2020-04-16T16:36:23.138Z Electron: 7.1.11 Chrome: 78.0.3904.130 Node.js: 12.8.1 V8: 7.8.279.23-electron.0 OS: Windows_NT x64 10.0.19044 项目…

Vue+springboot篮球文化体育商城商铺系统的设计与实现

随着生活和工作方面的压力逐渐增加,人们对网购的依赖和需求也就变得越来越大。篮球文化商铺是一个能使得人在繁忙或者不喜欢出门的人,足不出户而又很方便地购买到自己喜爱的篮球商品,这样既满足了自己的心灵需要,也不会影响到工作…

chatgpt赋能python:Python几次方介绍

Python几次方介绍 Python是一种通用编程语言,具有快速开发和易于阅读的优点。Python具有各种强大的功能,包括能够计算数值的能力。本文将介绍Python几次方的相关知识。 Python几次方的语法 要在Python中进行几次方运算,可以使用双星号&…

15、Redis数据库

1、数据库相关知识简介 1、数据库分类 关系型数据库:Oracle,MySQL,SqlServer,DB2NoSql数据库:NoSQL最常见的解释是“non-relational”, “Not Only SQL”也被很多人接受。NoSQL仅仅是一个概念,泛指非关系型的数据库,…

在ArcGIS中制作一幅“合格”的地图

在现代化的社会中,对各种不同类型的专题地图的需求以及使用更加地频繁,在很多小伙伴的论文写作过程中是不是也需要一张美观大方的研究区概况图呢?答案是肯定的。除了研究区概况图之外,很多地理空间相关的表达都可以借助ArcGIS软件…

电子企业搭建MES生产管理系统的解决方案

随着科技的不断进步,电子企业的发展也日益迅速。然而,在生产管理过程中,电子企业面临着许多挑战,如生产流程复杂、产品质量不易控制、生产效率低下等问题。为解决这些问题,电子企业需要搭建MES生产管理系统。本文将探讨…

WordPress网站如何在文章内容中插入广告或其他内容

您想在 WordPress 的帖子内容中插入广告吗? 您的帖子内容是访问者与您的网站最互动的地方,这使其成为展示广告的理想场所。 在本文中,我们将展示三种在 WordPress 的帖子内容中插入广告的方法。 本文网址: https://wpzhanzhang.eastfu.com…