单例模式 rust和java的实现

文章目录

    • 单例模式
      • 介绍
        • 应用实例:
        • 优点
        • 使用场景
      • 架构图
      • JAVA 实现
        • 单例模式的几种实现方式
      • rust实现
    • rust代码仓库

单例模式

单例模式(Singleton Pattern)是最简单的设计模式之一。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。

这种模式涉及到一个单一的类,该类负责创建自己的对象,同时确保只有单个对象被创建。这个类提供了一种访问其唯一的对象的方式,可以直接访问,不需要实例化该类的对象。

单例模式是一种创建型设计模式,它确保一个类只有一个实例,并提供了一个全局访问点来访问该实例。

注意

  1. 单例类只能有一个实例。
  2. 单例类必须自己创建自己的唯一实例。
  3. 单例类必须给所有其他对象提供这一实例。

介绍

意图:保证一个类仅有一个实例,并提供一个访问它的全局访问点。

主要解决:一个全局使用的类频繁地创建与销毁。

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

如何解决:判断系统是否已经有这个单例,如果有则返回,如果没有则创建。

关键代码:构造函数是私有的。

应用实例:
  1. 一个班级只有一个班主任。
  2. Windows 是多进程多线程的,在操作一个文件的时候,就不可避免地出现多个进程或线程同时操作一个文件的现象,所以所有文件的处理必须通过唯一的实例来进行。
  3. 一些设备管理器常常设计为单例模式,比如一个电脑有两台打印机,在输出的时候就要处理不能两台打印机打印同一个文件。
优点
  1. 在内存里只有一个实例,减少了内存的开销,尤其是频繁的创建和销毁实例(比如管理学院首页页面缓存)。
  2. 避免对资源的多重占用(比如写文件操作)。
    缺点:没有接口,不能继承,与单一职责原则冲突,一个类应该只关心内部逻辑,而不关心外面怎么样来实例化。
使用场景
  1. 要求生产唯一序列号。
  2. WEB 中的计数器,不用每次刷新都在数据库里加一次,用单例先缓存起来。
  3. 创建的一个对象需要消耗的资源过多,比如 I/O 与数据库的连接等。

架构图

我们将创建一个 SingleObject 类。SingleObject 类有它的私有构造函数和本身的一个静态实例。

SingleObject 类提供了一个静态方法,供外界获取它的静态实例。SingletonPatternDemo 类使用 SingleObject 类来获取 SingleObject 对象。

单例模式的 UML 图
在这里插入图片描述

步骤 1

JAVA 实现

创建一个 Singleton 类。
SingleObject.java

public class SingleObject {//创建 SingleObject 的一个对象private static SingleObject instance = new SingleObject();//让构造函数为 private,这样该类就不会被实例化private SingleObject(){}//获取唯一可用的对象public static SingleObject getInstance(){return instance;}public void showMessage(){System.out.println("Hello World!");}
}

步骤 2
从 singleton 类获取唯一的对象。

SingletonPatternDemo.java

public class SingletonPatternDemo {public static void main(String[] args) {//不合法的构造函数//编译时错误:构造函数 SingleObject() 是不可见的//SingleObject object = new SingleObject();//获取唯一可用的对象SingleObject object = SingleObject.getInstance();//显示消息object.showMessage();}
}

步骤 3
执行程序,输出结果:

Hello World!

单例模式的几种实现方式

单例模式的实现有多种方式,如下所示:

1、懒汉式,线程不安全

  • 是否 Lazy 初始化:是
  • 是否多线程安全:否
  • 实现难度:易
  • 描述:这种方式是最基本的实现方式,这种实现最大的问题就是不支持多线程。因为没有加锁 synchronized,所以严格意义上它并不算单例模式。
    这种方式 lazy loading 很明显,不要求线程安全,在多线程不能正常工作。

实例

public class Singleton {  private static Singleton instance;  private Singleton (){}  public static Singleton getInstance() {  if (instance == null) {  instance = new Singleton();  }  return instance;  }  
}

接下来介绍的几种实现方式都支持多线程,但是在性能上有所差异。

2、懒汉式,线程安全

  • 是否 Lazy 初始化:是
  • 是否多线程安全:是
  • 实现难度:易
  • 描述:这种方式具备很好的 lazy loading,能够在多线程中很好的工作,但是,效率很低,99% 情况下不需要同步。
  • 优点:第一次调用才初始化,避免内存浪费。
  • 缺点:必须加锁 synchronized 才能保证单例,但加锁会影响效率。

实例

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

3、饿汉式

  • 是否 Lazy 初始化:否
  • 是否多线程安全:是
  • 实现难度:易
  • 描述:这种方式比较常用,但容易产生垃圾对象。
  • 优点:没有加锁,执行效率会提高。
  • 缺点:类加载时就初始化,浪费内存。

它基于 classloader 机制避免了多线程的同步问题,不过,instance 在类装载时就实例化,虽然导致类装载的原因有很多种,在单例模式中大多数都是调用 getInstance 方法, 但是也不能确定有其他的方式(或者其他的静态方法)导致类装载,这时候初始化 instance 显然没有达到 lazy loading 的效果。

实例

public class Singleton {  private static Singleton instance = new Singleton();  private Singleton (){}  public static Singleton getInstance() {  return instance;  }  
}

4、双检锁/双重校验锁(DCL,即 double-checked locking)

  • 是否 Lazy 初始化:是
  • 是否多线程安全:是
  • 实现难度:较复杂
  • 描述:这种方式采用双锁机制,安全且在多线程情况下能保持高性能。

实例

public class Singleton {  private volatile static Singleton singleton;  private Singleton (){}  public static Singleton getSingleton() {  if (singleton == null) {  synchronized (Singleton.class) {  if (singleton == null) {  singleton = new Singleton();  }  }  }  return singleton;  }  
}

5、登记式/静态内部类

  • 是否 Lazy 初始化:是
  • 是否多线程安全:是
  • 实现难度:一般
  • 描述:这种方式能达到双检锁方式一样的功效,但实现更简单。对静态域使用延迟初始化,应使用这种方式而不是双检锁方式。这种方式只适用于静态域的情况,双检锁方式可在实例域需要延迟初始化时使用。

这种方式同样利用了 classloader 机制来保证初始化 instance 时只有一个线程,它跟第 3 种方式不同的是:第 3 种方式只要 Singleton 类被装载了,那么 instance 就会被实例化(没有达到 lazy loading 效果),而这种方式是 Singleton 类被装载了,instance 不一定被初始化。因为 SingletonHolder 类没有被主动使用,只有通过显式调用 getInstance 方法时,才会显式装载 SingletonHolder 类,从而实例化 instance。想象一下,如果实例化 instance 很消耗资源,所以想让它延迟加载,另外一方面,又不希望在 Singleton 类加载时就实例化,因为不能确保 Singleton 类还可能在其他的地方被主动使用从而被加载,那么这个时候实例化 instance 显然是不合适的。这个时候,这种方式相比第 3 种方式就显得很合理。

实例

public class Singleton {  private static class SingletonHolder {  private static final Singleton INSTANCE = new Singleton();  }  private Singleton (){}  public static final Singleton getInstance() {  return SingletonHolder.INSTANCE;  }  
}

6、枚举

  • 是否 Lazy 初始化:否
  • 是否多线程安全:是
  • 实现难度:易
  • 描述:这种实现方式还没有被广泛采用,但这是实现单例模式的最佳方法。它更简洁,自动支持序列化机制,绝对防止多次实例化。
    这种方式是 Effective Java 作者 Josh Bloch 提倡的方式,它不仅能避免多线程同步问题,而且还自动支持序列化机制,防止反序列化重新创建新的对象,绝对防止多次实例化。不过,由于 JDK1.5 之后才加入 enum 特性,用这种方式写不免让人感觉生疏,在实际工作中,也很少用。
    实例
public enum Singleton {  INSTANCE;  public void whateverMethod() {  }  
}

rust实现

在用rust进行懒加载时,最方便的还是用第三方库,本人运用的是lazy_static库,在用这社区库时,需要引入在Cargo.toml文件下的[dependencies]项目下引lazy_static = “1.4.0”。如图
在这里插入图片描述

use std::sync::{Arc, Mutex};
use lazy_static::lazy_static;struct Singleton;impl Singleton {//关联方法, 获取单例实例的方法fn get_instance() -> Arc<Mutex<Singleton>> {// 使用懒加载创建单例实例// 这里使用了 Arc 和 Mutex 来实现线程安全的单例// 只有第一次调用 get_instance 时会创建实例,之后都会返回已创建的实例static mut INSTANCE: Option<Arc<Mutex<Singleton>>> = None;//静态初始化,只运行一次unsafe {// get_or_insert_with ,如果是 None ,则将从data计算的值插入选项中,然后返回对包含值的可变引用。INSTANCE.get_or_insert_with(|| {Arc::new(Mutex::new(Singleton {}))}).clone()}}fn show_message(&self){println!("Hello World!");}
}// 使用lazy_static的懒加载
struct SingletonL;
impl SingletonL {fn show_message(&self){println!("Hello World!");}
}
lazy_static! {static ref INSTANCE: Mutex<SingletonL> = Mutex::new(SingletonL {   });
}fn main() {// 获取单例实例,自定义let instance1 = Singleton::get_instance();// 修改单例数据{let instance = instance1.lock().unwrap();instance.show_message();}// 获取单例实例,社区lazy_staticlet instance = INSTANCE.lock().unwrap();instance.show_message();
}

rust代码仓库

https://github.com/onenewcode/design.git
本教程项目在bin文件夹下的singleobject.rs文件中

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

rabbitMQ rascal/amqplib报错 Error: Unexpected close 排查

以下是一些可能导致此 RabbitMQ 客户端或任何其他 RabbitMQ 客户端中的套接字读取或写入失败的常见场景 1.错过&#xff08;客户端&#xff09;心跳 第一个常见原因是RabbitMQ 检测到心跳丢失。发生这种情况时&#xff0c;RabbitMQ 将添加一个有关它的日志条目&#xff0c;然…

SQL note1:Basic Queries + Joins Subqueries

目录 一、Basic Queries 1、数据库术语 2、查表 3、过滤掉我们不感兴趣的行 4、布尔运算 5、过滤空值&#xff08;NULL&#xff09; 6、分组和聚合 1&#xff09;汇总数据的列 2&#xff09;汇总数据组 7、分组聚合的警告 1&#xff09;SELECT age, AVG(num_dogs) FR…

基于ssm的大学生社团管理系统

基于ssm的大学生社团管理系统 摘要 基于SSM的大学生社团管理系统是一个全面、高效的社团管理平台&#xff0c;旨在帮助大学生和社团管理员更方便、更快捷地进行社团活动的组织和管理。该系统基于Spring、SpringMVC和MyBatis&#xff08;简称SSM&#xff09;开发&#xff0c;这三…

Ubuntu中安装rabbitMQ

一、安装 RabbitMQ ①&#xff1a;更新源 sudo apt-get update②&#xff1a;安装Rrlang语言 由于RabbitMq需要erlang语言的支持&#xff0c;在安装RabbitMq之前需要安装erlang sudo apt-get install erlang-nox③&#xff1a;安装rabbitMQ sudo apt-get install rabbitmq-s…

【算法与数据结构】216、LeetCode组合总和 III

文章目录 一、题目二、解法三、完整代码 所有的LeetCode题解索引&#xff0c;可以看这篇文章——【算法和数据结构】LeetCode题解。 一、题目 二、解法 思路分析&#xff1a;本题可以直接利用77题的代码【算法与数据结构】77、LeetCode组合&#xff0c;稍作修改即可使用。   …

APISpace IP归属地查询接口案例代码

1.IP归属地查询API 1.1 API接口简介 IP归属地查询API&#xff1a;根据IP地址查询归属地信息&#xff0c;包含国家、省、市、区县和运营商等信息。APISpace 提供了IPv4 和 IPv6 的IP归属地查询接口&#xff0c;并且包含了各种归属地精度查询的接口。 1.2 IPv4 IPv4归属地查询…

亚马逊云科技海外服务器初体验

目录 前言亚马逊云科技海外服务器概述注册使用流程实例创建性能表现用户体验服务支持初体验总结 前言 随着云原生技术的飞速发展&#xff0c;越来越多的企业和开发者选择云服务器来作为自己的使用工具&#xff0c;云原生技术的发展也促进了云服务厂商的产品发展&#xff0c;所…

树莓派4B的测试记录(CPU、FFMPEG)

本文是用来记录树莓派 4B 的一些测试记录。 温度 下面记录中的风扇和大风扇是这样的&#xff1a; 为什么要用大风扇呢&#xff1f;因为小风扇在外壳上&#xff0c;气流通过外壳的珊格会有啸叫&#xff0c;声音不大但是很烦人&#xff0c;大风扇没这个问题&#xff0c;并且同样…

python编程复习系列——week1(Input Output)

Input & Output 前言0、我们的第一个Python程序一、变量和数据类型1.变量是用来存储值的保留存储位置2.变量以特定的数据类型存储值。常见数据类型&#xff1a;3.字符串添加&#xff08;连接&#xff09;4.字符串乘法&#xff08;带数字&#xff09;&#xff01;5.从用户处…

vue3 - swiper插件 实现PC端的 视频滑动功能(仿抖音短视频)

swiper官网 ​​​​​​swiper属性/组件查询 vue中使用swiper 步骤&#xff1a; ① npm install swiper 安装 ② 基础模板&#xff1a; <div><swiper class"swiper-box" :direction"vertical":grabCursor"true" :mousewheel"tr…

【面试经典150 | 】颠倒二进制位

文章目录 写在前面Tag题目来源题目解读解题思路方法一&#xff1a;逐位颠倒方法二&#xff1a;分治 写在最后 写在前面 本专栏专注于分析与讲解【面试经典150】算法&#xff0c;两到三天更新一篇文章&#xff0c;欢迎催更…… 专栏内容以分析题目为主&#xff0c;并附带一些对于…

线程基础知识

目录 进程 线程 CPU 核心数和线程数的关系 上下文切换(Context switch) Thread 和 Runnable 的区别 Callable、Future 和 FutureTask 面试题:新启线程有几种方式? 中止 中断 深入理解 run()和 start() 进程 我们常听说的是应用程序&#xff0c;也就是 app&#xff…

使命担当 守护安全 | 中睿天下获全国海关信息中心感谢信

近日&#xff0c;全国海关信息中心向中睿天下发来感谢信&#xff0c;对中睿天下在2023年网络攻防演练专项活动中的大力支持和优异表现给予了高度赞扬。 中睿天下对此次任务高度重视&#xff0c;紧密围绕全国海关信息中心的行动要求&#xff0c;发挥自身优势有效整合资源&#x…

Vue3中使用Pinia

前言&#xff1a; 在 Vue 3 中&#xff0c;Pinia 是一个用于管理全局状态的库。它可以让我们更容易地维护和共享应用的状态。下面是如何在 Vue 3 中使用 Pinia 的步骤。 正文&#xff1a; 首先&#xff0c;我们需要安装 Pinia。可以使用 npm 或者 yarn 来安装。例如&#xff0…

【Unity ShaderGraph】| 如何快速制作一个炫酷的 全息投影效果

前言 【Unity ShaderGraph】| 如何快速制作一个炫酷的 全息投影效果一、效果展示二、 全息投影效果 前言 本文将使用ShaderGraph制作一个 炫酷的 全息投影效果 &#xff0c;可以直接拿到项目中使用。对ShaderGraph还不了解的小伙伴可以参考这篇文章&#xff1a;【Unity Shader…

Docker学习——④

文章目录 1、Docker Image&#xff08;镜像&#xff09;2、镜像命令详解2.1 docker rmi2.2 docker save2.3 docker load2.4 docker image inspect2.5 docker history2.6 docker image prune 3、镜像综合实战3.1 离线镜像迁移3.2 镜像存储的压缩与共享 1、Docker Image&#xff…

创建多层级行索引,创建多层级行索引的DataFrameMultiIndex.from_product()

【小白从小学Python、C、Java】 【计算机等考500强证书考研】 【Python-数据分析】 创建多层级行索引, 创建多层级行索引的DataFrame MultiIndex.from_product() [太阳]选择题 使用pd.MultiIndex.from_product()&#xff0c;下列输出正确的是&#xff1a; import pandas as pd…

C++打怪升级(十)- STL之vector

~~~~ 前言1. vector 是什么2. 见见vector的常用接口函数吧构造函数无参构造函数使用n个val构造拷贝构造使用迭代器范围构造初始化形参列表构造 析构函数赋值运算符重载函数元素访问[]运算符重载函数访问at函数访问front函数back函数 迭代器相关正向迭代器反向迭代器 容量相关si…

C# OpenCvSharp 玉米粒计数

效果 项目 代码 using OpenCvSharp; using System; using System.Drawing; using System.Text; using System.Windows.Forms;namespace OpenCvSharp_Demo {public partial class frmMain : Form{public frmMain(){InitializeComponent();}string fileFilter "*.*|*.bmp;…

【NLP】特征提取: 广泛指南和 3 个操作教程 [Python、CNN、BERT]

什么是机器学习中的特征提取&#xff1f; 特征提取是数据分析和机器学习中的基本概念&#xff0c;是将原始数据转换为更适合分析或建模的格式过程中的关键步骤。特征&#xff0c;也称为变量或属性&#xff0c;是我们用来进行预测、对对象进行分类或从数据中获取见解的数据点的…