单例模式的八种写法比较

2023-11-19

单例模式是最常用到的设计模式之一,熟悉设计模式的朋友对单例模式都不会陌生。一般介绍单例模式的书籍都会提到 饿汉式 和 懒汉式 这两种实现方式。但是除了这两种方式,本文还会介绍其他几种实现单例的方式,让我们来一起看看吧。

 

简介

单例模式是一种常用的软件设计模式,其定义是单例对象的类只能允许一个实例存在。

许多时候整个系统只需要拥有一个的全局对象,这样有利于我们协调系统整体的行为。比如在某个服务器程序中,该服务器的配置信息存放在一个文件中,这些配置数据由一个单例对象统一读取,然后服务进程中的其他对象再通过这个单例对象获取这些配置信息。这种方式简化了在复杂环境下的配置管理。

基本的实现思路

单例模式要求类能够有返回对象一个引用(永远是同一个)和一个获得该实例的方法(必须是静态方法,通常使用getInstance这个名称)。

单例的实现主要是通过以下两个步骤:

  1. 将该类的构造方法定义为私有方法,这样其他处的代码就无法通过调用该类的构造方法来实例化该类的对象,只有通过该类提供的静态方法来得到该类的唯一实例;
  2. 在该类内提供一个静态方法,当我们调用这个方法时,如果类持有的引用不为空就返回这个引用,如果类保持的引用为空就创建该类的实例并将实例的引用赋予该类保持的引用。

注意事项

单例模式在多线程的应用场合下必须小心使用。如果当唯一实例尚未创建时,有两个线程同时调用创建方法,那么它们同时没有检测到唯一实例的存在,从而同时各自创建了一个实例,这样就有两个实例被构造出来,从而违反了单例模式中实例唯一的原则。 解决这个问题的办法是为指示类是否已经实例化的变量提供一个互斥锁(虽然这样会降低效率)。

单例模式的八种写法

1、饿汉式(静态常量)[可用]

public class Singleton {

    private final static Singleton INSTANCE = new Singleton();

    private Singleton(){}

    public static Singleton getInstance(){
        return INSTANCE;
    }
}

优点:这种写法比较简单,就是在类装载的时候就完成实例化。避免了线程同步问题。

缺点:在类装载的时候就完成实例化,没有达到Lazy Loading的效果。如果从始至终从未使用过这个实例,则会造成内存的浪费。

2、饿汉式(静态代码块)[可用]

public class Singleton {

    private static Singleton instance;

    static {
        instance = new Singleton();
    }

    private Singleton() {}

    public static Singleton getInstance() {
        return instance;
    }
}

这种方式和上面的方式其实类似,只不过将类实例化的过程放在了静态代码块中,也是在类装载的时候,就执行静态代码块中的代码,初始化类的实例。优缺点和上面是一样的。

3、懒汉式(线程不安全)[不可用]

public class Singleton {

    private static Singleton singleton;

    private Singleton() {}

    public static Singleton getInstance() {
        if (singleton == null) {
            singleton = new Singleton();
        }
        return singleton;
    }
}

这种写法起到了Lazy Loading的效果,但是只能在单线程下使用。如果在多线程下,一个线程进入了if (singleton == null)判断语句块,还未来得及往下执行,另一个线程也通过了这个判断语句,这时便会产生多个实例。所以在多线程环境下不可使用这种方式。

4、懒汉式(线程安全,同步方法)[不推荐用]

public class Singleton {

    private static Singleton singleton;

    private Singleton() {}

    public static synchronized Singleton getInstance() {
        if (singleton == null) {
            singleton = new Singleton();
        }
        return singleton;
    }
}

解决上面第三种实现方式的线程不安全问题,做个线程同步就可以了,于是就对getInstance()方法进行了线程同步。

缺点:效率太低了,每个线程在想获得类的实例时候,执行getInstance()方法都要进行同步。而其实这个方法只执行一次实例化代码就够了,后面的想获得该类实例,直接return就行了。方法进行同步效率太低要改进。

5、懒汉式(线程安全,同步代码块)[不可用]

public class Singleton {

    private static Singleton singleton;

    private Singleton() {}

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

由于第四种实现方式同步效率太低,所以摒弃同步方法,改为同步产生实例化的的代码块。但是这种同步并不能起到线程同步的作用。跟第3种实现方式遇到的情形一致,假如一个线程进入了if (singleton == null)判断语句块,还未来得及往下执行,另一个线程也通过了这个判断语句,这时便会产生多个实例。

6、双重检查[推荐用]

public class Singleton {

    private static volatile Singleton singleton;

    private Singleton() {}

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

Double-Check概念对于多线程开发者来说不会陌生,如代码中所示,我们进行了两次if (singleton == null)检查,这样就可以保证线程安全了。这样,实例化代码只用执行一次,后面再次访问时,判断if (singleton == null),直接return实例化对象。

优点:线程安全;延迟加载;效率较高。

7、静态内部类[推荐用]

public class Singleton {

    private Singleton() {}

    private static class SingletonInstance {
        private static final Singleton INSTANCE = new Singleton();
    }

    public static Singleton getInstance() {
        return SingletonInstance.INSTANCE;
    }
}

这种方式跟饿汉式方式采用的机制类似,但又有不同。两者都是采用了类装载的机制来保证初始化实例时只有一个线程。不同的地方在饿汉式方式是只要Singleton类被装载就会实例化,没有Lazy-Loading的作用,而静态内部类方式在Singleton类被装载时并不会立即实例化,而是在需要实例化时,调用getInstance方法,才会装载SingletonInstance类,从而完成Singleton的实例化。

类的静态属性只会在第一次加载类的时候初始化,所以在这里,JVM帮助我们保证了线程的安全性,在类进行初始化时,别的线程是无法进入的。

优点:避免了线程不安全,延迟加载,效率高。

8、枚举[推荐用]

public enum Singleton {
    INSTANCE;
    public void whateverMethod() {

    }
}

借助JDK1.5中添加的枚举来实现单例模式。不仅能避免多线程同步问题,而且还能防止反序列化重新创建新的对象。可能是因为枚举在JDK1.5中才添加,所以在实际项目开发中,很少见人这么写过。

优点

系统内存中该类只存在一个对象,节省了系统资源,对于一些需要频繁创建销毁的对象,使用单例模式可以提高系统性能。

缺点

当想实例化一个单例类的时候,必须要记住使用相应的获取对象的方法,而不是使用new,可能会给其他开发人员造成困扰,特别是看不到源码的时候。

适用场合

  • 需要频繁的进行创建和销毁的对象;
  • 创建对象时耗时过多或耗费资源过多,但又经常用到的对象;
  • 工具类对象;
  • 频繁访问数据库或文件的对象。

 

转载地址:https://www.cnblogs.com/zhaoyan001/p/6365064.html

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

单例模式的八种写法比较 的相关文章

  • Spring Security 通过并发登录尝试将用户锁定

    我是安全新手 遇到了一个问题 该问题导致用户帐户被锁定 只有重新启动应用程序才能修复它 我有一个带有 spring security 4 0 2 RELEASE 应用程序的 spring boot 1 3 0 BUILD SNAPSHOT
  • 无法访问类型的封闭实例。 [复制]

    这个问题在这里已经有答案了 整个代码是 public class ThreadLocalTest ThreadLocal
  • Java-线程与CPU的关系

    我对多线程还很陌生 我正在开发一个项目 尝试在我的 Java 程序中使用 4 个 CPU 我想做类似的事情 int numProcessors Runtime getRuntime availableProcessors ExecutorS
  • 如何迭代所有注册表项?

    我正在尝试迭代所有注册表项以查找 包含 并删除 jre1 5 0 14 值 有办法做到吗 下面的代码只是在特定键下找到jre1 5 0 14 我确实想迭代所有的键 顺便说一句 if 子句获取是否等于 jre1 5 0 14 但如果它包含 j
  • 在Java中清空数组/处理

    除了循环遍历数组中的每个元素并将每个元素设置为 null 之外 Java 处理中是否有一个本机函数可以简单地清空数组 或销毁它 以便能够将其重新声明为新数组 There s Arrays fill myArray null 并不是说它执行的
  • 使用 Android WebViewClient 启用特定 SSL 协议

    我的应用程序使用WebViewClient与服务器建立 SSL 连接 服务器配置为仅接受 TLSv1 1 及以上协议 使用 Android 时 如何检查哪些 SSL 协议是 a 支持的和 b 默认启用的WebViewClient在设备上 如
  • 静态方法的 Java 内存模型

    我来自操作系统和 C 语言背景 在代码编译时 世界很简单 需要处理和理解堆栈 堆文本部分等 当我开始学习 Java 时 我确实了解 JVM 和垃圾收集器 我对静态方法感到很有趣 根据我的理解 类的所有实例都会在堆中创建 然后被清理 但是 对
  • firebase推送通知错误Spring Boot服务器端

    我正在尝试从 Spring Boot 服务器端发送通知到客户端 android 服务器运行良好 一切都很好 2020 09 01 08 13 07 691 INFO 18941 restartedMain e DevToolsPropert
  • 正确使用 JDBC 连接池 (Glassfish)

    我需要在 Java Web 服务中作为会话 bean 实现数据库连接 但我不确定我这样做是否正确 我创建了一个类 public final class SQLUtils private static DataSource m ds null
  • 尝试在java中的Arraylist中查找对象的所有出现

    我有一个 Java ArrayList 我需要查找其中出现的所有特定对象 ArrayList indexOf Object 方法只找到一次出现 所以看来我还需要其他东西 我认为你不需要太花哨 以下应该可以正常工作 static
  • Java元数据读写

    是否可以以通用方式 对于所有图像类型 在 Java 中读取和写入元数据 我找到了一些示例 但它们总是特定的 例如 JPEG 或 PNG 我需要一些足够通用的东西 而不是到处都有 if else 语句 我不想重写源代码 但这是一个很好的例子
  • Java:使用 Java.util.concurrent 线程访问读取线程串行端口

    我正在尝试编写一个 Java 串行设备驱动程序并想使用 对我来说是新的 java util concurrent包裹 我有一种发送数据包然后等待 ACK 的方法 我打算有炭 接收在不同的线程中运行 如果接收线程收到 ACK 它应该使用发送数
  • 在 eclipse 之外将 Spring MVC 应用程序部署到 tomcat 的幕后会发生什么?

    我猜想使用像 eclipse 这样很棒的 IDE 的一个缺点是你会忽略应用程序幕后发生的事情 我是一名 Ruby 开发人员 所以不是一名 Java 老手 所以我一直在用 java 编写一个项目 并使用 spring 框架进行 IOC 和 M
  • 如何通过子 POJO 的属性过滤复合 ManyToMany POJO?

    我有两个像这样的房间实体 Entity public class Teacher implements Serializable PrimaryKey autoGenerate true public int id ColumnInfo n
  • 为什么现在()? (客观化)

    为什么我想要异步加载 Objectify 实体 异步加载到底意味着什么 根据客观化有关加载的文档 https code google com p objectify appengine wiki BasicOperations Loadin
  • 是否可以为 azure blob 存储中的给定目录生成具有写入权限的 SAS(共享访问签名)

    我们的 blob 存储帐户结构 容器名称 simple 在这个容器内我们有 blob aa one zip aa two zip bb ss zip bb dd zip 是否可以生成对aa 目录 有写权限 但对bb 目录 没有访问权限的SA
  • 在Java中多次读取System.in会导致IOException?

    我正在尝试创建一个小命令行游戏来强化我在过去几个月中在 Java 中学到的一些东西 我正在尝试创建一个名为 readInput 的方法 它返回一个我可以一次又一次调用的字符串 第一次它工作正常 但第二次它会导致 IO Exception 如
  • 战争库中的罐子爆炸

    我们可以将分解的 jar 文件放入 war web inf 库中吗 它在 JBOSS 4 2 中对我不起作用 我收到以下错误并且无法部署应用程序 Caused by javax management RuntimeOperationsExc
  • H2 - (相当)长的 INSERT 失败,错误 42000

    H2 内存中 插入 错误 42000 尝试过版本 1 4 196 1 4 197 1 4 199 我还尝试在 H2 服务器 本地 上执行 INSERT 也失败 给出错误的行 抱歉 但出于安全原因 我无法生成更多 INSERT INTO tb
  • 将带有 webapp 的 WAR 部署到 Maven 中央存储库是否有意义?

    这样做有意义吗 如果是 我在哪里可以找到使用简单的 Web Hello World 执行此操作的示例 当人们从 Maven 执行 Web 应用程序时 他们会使用 Jetty 来运行它吗 我想 tomcat 太重了 任何帮助将不胜感激 谢谢

随机推荐

  • jQuery ajax(自动补全)

    1 Ajax了解 Ajax 是一种用于创建快速动态网页的技术 1 1为什么用Ajax 无刷新 不刷新整个网页 只刷新局部 无刷新的好处 值更新部分页面 有效利用宽带 提高用户体验 2 Ajax用来做什么 用来刷新局部页面 不使用 Ajax
  • 镜之Json Compare Diff

    前言 镜 寓意是凡事都有两面性 Json 对比也不例外 因公司业务功能当中有一个履历的功能 它有多个版本的 JSON 数据需要对比出每个版本的不同差异节点并且将差异放置在一个新的 JSON 当中原有结构不能变动 差异节点使用数组对象的形式存
  • Unity3D 官方文档 平台特殊化之Standalone和多显示器的处理

    版本 unity 5 6 语言 C 总起 Standalone 我一眼看下去的理解就是PC机平台 官方文档中说该平台指的是Mac Windows和Linux 这篇文章主要讲的是Standalone平台下的设置 通过点击菜单栏的File gt
  • linux cat命令使用方法,Linux cat命令怎么使用

    作为一个典型的DOS 和WINDOWS用户 让我们一起来学习Linux的一些主要命令 下面跟着学习啦小编来一起了解下Linux cat命令怎么使用吧 Linux cat命令使用方法 cat命令主要用来查看文件内容 创建文件 文件合并 追加文
  • MySQL数据库被攻击,被删库勒索,逼迫我使出洪荒之力进行恢复数据

    昨天连夜赶了一篇文章 讲述了一个被黑客连续攻击服务器三次的普通 搬砖人 一次比一次艰难 一次比一次狠 我给大家看几张图 看看黑客的 佳作 首先创建一个数据库 README FHX 然后创建表 README 插入一条数据 内容如下 内容 以下
  • 学生信息后台管理系统(GUI)

    一 目的 通过制作学生信息后台管理系统熟悉java中JDBC和CUI 图形用户接口 的使用 二 实验工具 1 Eclipse IDE Version 2020 12 4 18 0 2 mysql 3 Navicat Premium 15 数
  • string常见接口的使用(基于c++标准库中的STL)

    前言 string是c 中常见的容器 它是用来管理字符的 它在物理上是可以动态增长的线性表 对于了解它的使用 以及常见的接口使用对于我们日常开发和使用是很有必要的 所以接下来让我们一起来了解一下string常见的接口吧 目录 1 strin
  • 线程池用例

    线程池逻辑类 public class TaskExecutorService private final ExecutorService pool private final ThreadPoolExecutor pool private
  • HTML 5 标签、属性、事件及浏览器兼容性速查表

    HTML 5 可以说是近十年来 Web 标准最巨大的飞跃 和以前的版本不同 HTML 5 并非仅仅用来表示 Web 内容 它的使命是将 Web 带入一个成熟的应用平台 在这个平台上 视频 音频 图象 动画 以及同电脑的交互都被标准化 尽管
  • 相比引流,期货公司更应该借助私域提升留存和转化

    近期 我们和很多期货公司都有过交流和沟通 相较于如何提升产品留存和转化 大家似乎更关注如何引流 我理解大家对流量获取的焦虑 但回归运营的底层逻辑 产品的留存和转化其实更为重要 现如今很多期货公司已陆续借助企业微信搭建私域流量池 虽然了解了市
  • VFloss pytorch

    Loss functions import torch import torch nn as nn from utils general import bbox iou from utils torch utils import is pa
  • Unity3D之Rigidbody

    目录 常用的Rigidbody属性和方法 rigidbody AddForce rigidbody AddTorque rigidbody velocity rigidbody angularVelocity rigidbody Sleep
  • 国家语言对照表

    国家 地区 语言代码 国家 地区 语言代码 简体中文 中国 zh cn 繁体中文 台湾地区 zh tw 繁体中文 香港 zh hk 英语 香港 en hk 英语 美国 en us 英语 英国 en gb 英语 全球 en ww 英语 加拿大
  • Spring源码从入门到精通---@Scope&@Lazy(三)

    上篇文章主要介绍了 ComponentScan的注解 Spring源码从入门到精通 ComponentScan 二 这篇文章主要介绍单例模式 多例模式 懒加载 先上目录结构 这篇文章先创建了beanConfig2文件 1 多例模式 单例模式
  • Compile Options--编译选项

    目的 其主要作用是用于调试跟踪和测试 主要包含 MT TASK MT ZDO FUNC and other MT compile options LCD SUPPORTED LCD SUPPORTED DEBUG BLINK LEDS 且看
  • 【产量预测】BP和GRNN神经网络预测粮食产量【含Matlab源码 1247期】

    一 BP神经网络简介 1 BP神经网络概述 BP Back Propagation 神经网络是1986年由Rumelhart和McCelland为首的科研小组提出 参见他们发表在Nature上的论文 Learning representat
  • 第二章 常用安全工具

    目录 1 Kali系统工具分类 2 Kali Top10工具 1 Kali系统工具分类 信息收集 Information Gathering 主要目的是收集渗透测试目标的基本信息 包括操作系统信息 网络配置信息 应用服务信息等 脆弱性分析
  • Python:读取CSV文件的某几列

    三种读取方式如下 import csv import pandas as pd with open 2 csv r as csvfile reader csv reader csvfile column1 row 1 for row in
  • 【Docker应用篇】GitLab代码私服

    Docker应用篇 GitLab代码私服 什么是GitLab 概述 基于 Docker 安装 GitLab 访问 GitLab 的账户管理 创建用户 设置账户信息 修改用户密码 退出并使用新账户登录 GitLab GitHub 使用 SSH
  • 单例模式的八种写法比较

    单例模式是最常用到的设计模式之一 熟悉设计模式的朋友对单例模式都不会陌生 一般介绍单例模式的书籍都会提到 饿汉式 和 懒汉式 这两种实现方式 但是除了这两种方式 本文还会介绍其他几种实现单例的方式 让我们来一起看看吧 简介 单例模式是一种常