【设计模式-2.1】创建型——单例模式

news2024/11/19 16:48:34

说明:设计模式根据用途分为创建型、结构性和行为型。创建型模式主要用于描述如何创建对象,本文介绍创建型中的单例模式。

饿汉式单例

单例模式是比较常见的一种设计模式,旨在确保对象的唯一性,什么时候去使用这个对象都是同一个。这样设计的目的是为了避免对象重复创建,浪费资源,同时也保证了对象的唯一性,不至于多个相同的对象,状态不一致的情况。

以下是单例模式的简单实现:

/**
 * 太阳类
 */
public class Sun {
    
    private static final Sun sun = new Sun();

    private Sun() {
    }

    public static Sun getInstance() {
        return sun;
    }
}

需要注意,对象的创建是在对象类内部,且为static final修饰,并且私有化构造方法,使其不能在外部被创建,其次创建一个静态方法,返回其对象

此时,外部通过类名.方法名的方式,可以访问到该对象,且始终为同一个;

public class Test {
    public static void main(String[] args) {
        Sun sun1 = Sun.getInstance();
        Sun sun2 = Sun.getInstance();
        System.out.println(sun1 == sun2);
    }
}

两次获取到的对象相同

在这里插入图片描述

上面这种创建方式,称为饿汉式单例(Eager Singleton),即类加载完成时就创建对象

懒汉式单例

以下这种方式,称为懒汉式单例(Lazy Singleton),在调用静态方法时才创建对象

/**
 * 懒汉式单例模式
 */
public class LazySun {
    
    private static LazySun sun = null;

    private LazySun() {
    }

    public static LazySun getInstance() {
        if (sun == null) {
            sun = new LazySun();
        }
        return sun;
    }
}

这种创建方式,可以实现延迟加载,节约资源。但是在多线程并发时,如果有多个线程在if (sun == null),就会导致对象被创建多次,所以我们很容易想到的是加锁,将静态方法加上锁,如下:

/**
 * 懒汉式单例模式
 */
public class LazySun {

    private static LazySun sun = null;

    private LazySun() {
    }

    public synchronized static LazySun getInstance() {
        if (sun == null) {
            sun = new LazySun();
        }
        return sun;
    }
}

但是,给方法加上锁后,会影响性能,可以考虑减少锁的范围,只锁住创建对象这一行,如下:

/**
 * 懒汉式单例模式
 */
public class LazySun {

    private static LazySun sun = null;

    private LazySun() {
    }

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

双重检查锁定

但是,还有问题。在多线程并发下,如果线程A在创建对象(未完成),线程B、C在if (sun == null) 这里判断为true,即便对象创建完成,线程B、C通过了if判断,也还是会依次再创建对象,也造成了对象被重复创建。因此,还需要改造,如下:

/**
 * 双重检查锁定
 */
public class LazySun {

    private volatile static LazySun sun = null;

    private LazySun() {
    }

    public static LazySun getInstance() {
        // 第一次判断
        if (sun == null) {
            synchronized (LazySun.class) {
                // 第二次判断
                if (sun == null){
                    sun = new LazySun();
                }
            }
        }
        return sun;
    }
}

就是在锁住的代码块里面再进行一次非空判断,称为双重检查锁定(Double-Check Locking),同时,单例对象修饰符加上volatile,确保多个线程都能正确处理;

IoDH

饿汉式单例,是在类被加载时就创建了对象。不需要考虑多线程,但是无论是否使用到对象都创建,造成了资源浪费。而懒汉式单例,虽然做到了延迟加载,但是需要处理好多线程情况下的对象创建,使用了锁,影响了性能。

那有没有一种更好了方式,既能在多线程下使用,又不会影响性能?

在《设计模式的艺术》(刘伟著)中作者提供了一种更好的创建方式,称为IoDH(Initialization on Demand Holder,按需初始化),代码如下:

/**
 * IoDH
 */
public class Singleton {

    private Singleton() {
    }

    private static class HolderClass {
        private final static Singleton instance = new Singleton();
    }

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

这种方式是对饿汉式单例的改进,是在单例类里创建了一个内部类,将单例对象的创建放在了这个内部类里面。因为单例对象不是单例类的一个成员变量,所以对象在类加载时不会被创建,而是会在调用静态方法时被创建,这样既能延迟加载,又没有使用锁,影响性能,一举两得。

书中说,通过使用IoDH,既可以实现延迟加载,又可以保证线程安全,不影响系统性能。因此,IoDH不失为一种最好的Java语言单例模式实现方式;其缺点是与编程语言本身的特性相关,很多面向对象语言不支持IoDH。

总结

本文参考《设计模式的艺术》、《秒懂设计模式》两书

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

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

相关文章

Vue19 列表过滤

直接上代码 以下代码使用了两种实现方式&#xff0c;监视属性和计算属性 当能用计算属性实现时&#xff0c;推荐使用计算属性 <!DOCTYPE html> <html><head><meta charset"UTF-8" /><title>列表过滤</title><script type&q…

xadmin后台在每一行记录增加一个复制链接按钮

xadmin后台在每一行记录增加一个复制链接按钮 1、效果 点击复制后,自动把url链接复制到粘贴板,按Ctrl+v即可显示复制内容。 2、实现代码 adminx.py # 用户管理 class UserWhiteListAdmin(object):search_fields = [name, mobile] # 检索字段list_display

【Flutter】设置顶部状态栏的显示、隐藏、半透明灰色显示

【Flutter】设置顶部状态栏的显示、隐藏、半透明灰色显示 设置方法&#xff1a; // 这种模式不现实状态栏 SystemChrome.setEnabledSystemUIMode(SystemUiMode.immersiveSticky); // 这种模式显示状态栏 SystemChrome.setEnabledSystemUIMode(SystemUiMode.edgeToEdge); // 修…

【C指针(五)】6种转移表实现整合longjmp()/setjmp()函数和qsort函数详解分析模拟实现

&#x1f308;write in front :&#x1f50d;个人主页 &#xff1a; 啊森要自信的主页 ✏️真正相信奇迹的家伙&#xff0c;本身和奇迹一样了不起啊&#xff01; 欢迎大家关注&#x1f50d;点赞&#x1f44d;收藏⭐️留言&#x1f4dd;>希望看完我的文章对你有小小的帮助&am…

2023 hnust 湖南科技大学 信息安全管理课程 期中考试 复习资料

前言 ※老师没画重点的补充内容★往年试卷中多次出现或老师提过的&#xff0c;很可能考该笔记是奔着及格线去的&#xff0c;不是奔着90由于没有听过课&#xff0c;部分知识点不一定全&#xff0c;答案不一定完全正确 题型 试卷有很多题是原题&#xff0c;分值是猜测的 判断题…

STM32 CAN协议讲解以及代码

STM32 CAN 文章目录 STM32 CAN前言一、CAN外设1.主控制寄存器CAN_MCR2.位时序寄存器CAN_BTR3.CAN的发送邮箱4.CAN的接收FIFO5.验收筛选器 二、代码配置1.初始化2.发送数据3.接收数据4.main.c 前言 前面学习了CAN的一些理论知识&#xff0c;他在我们的STM32里面是怎么用的呢 前…

JavaSE 知识点总结

路在脚下&#xff0c;行则将至 目录 1. 初始Java 1.1 Java之父——高斯林 1.2 一次编译&#xff0c;到处运行 1.3 注释 2. 数据结构与变量 2.1 数据类型 2.2 变量 2.3 常量 3. 运算符 3.1 逻辑与 && 3.2 逻辑 || 3.3 逻辑非 ! 3.4 特殊的位运算符(C语言没有)…

报表生成器Stimulsoft用户手册:具有交叉基元的报告

Stimulsoft Reports 是一款报告编写器&#xff0c;主要用于在桌面和Web上从头开始创建任何复杂的报告。可以在大多数平台上轻松实现部署&#xff0c;如ASP.NET, WinForms, .NET Core, JavaScript, WPF, Angular, Blazor, PHP, Java等&#xff0c;在你的应用程序中嵌入报告设计器…

E5052A/安捷伦Agilent E5052A信号源分析仪

181/2461/8938产品概述 是德科技E5052A(安捷伦)信号源分析仪&#xff0c;10 MHz至7 GHz&#xff0c;具有许多增强的性能特征。在表征VCO或其它类型的高频信号源、aw以及高速数据通信系统中的时钟抖动评估方面&#xff0c;它提供了世界上最高的测量吞吐量和最佳可用性。 是德科…

elementPlus之home页面布局

可以根据自己喜欢的格式选择 现在 header 部分 Aside 部分 Main部分 加上背景色以及命名 <template><div class="common-layout"><el-container><el-header class="homeHeader"><div class="headerTitle">Dev…

如何使用Python在3dMax控制网格对象?

我们以一个在3dMax中使用Python脚本在网格对象对象上创建水波变形作为例子。 首先&#xff0c;在3dmax创建两个对象&#xff0c;一个“box”对象&#xff0c;将长宽方向的分段设置的多一些&#xff08;目的是为了后面的水波变形&#xff09;&#xff0c;一个“点”帮助对象&am…

Linux内核中的overlay文件系统

一、简介 Docker 内核实现容器的功能用了linux 内核中的三个特性 Namespace、Cgroup、UnionFs&#xff0c;今天我们来说一下UnionFs。 linux UnionFs 实现的是overlay 文件系统 OverlayFs 文件系统分为三层&#xff0c; lower 是只读层 Upper 是可读写 Merged 是 lower 和U…

JVM虚拟机:G1垃圾回收器的日志分析

本文重点 本文我们将学习G1垃圾回收器的日志 使用 执行命令 java -Xms20M -Xmx20M -XX:PrintGCDetails -XX:UseG1GC 类名 分析 前面我们学习了G1垃圾回收器&#xff0c;它的回收有三种可能&#xff1a; YGC FGC MixedGC GC pause表示STW,Evacuation表示复制对象&#xff0c;…

【经典小练习】输出文件路径名

文章目录 &#x1f339;问题✨思路&#x1f354;代码&#x1f6f8;读取文件&#xff0c;并把文件名保存到文件中 对指定目录下的所有 Java 文件进行编译、打包等处理&#xff1b; 查找指定目录下所有包含特定字符串的 Java 文件&#xff1b; 统计指定目录下所有 Java 文件的行数…

【Docker】从零开始:12.容器数据卷

【Docker】从零开始&#xff1a;12.容器数据卷 1.什么是容器数据库卷2.数据的覆盖问题3.为什么要用数据卷4.Docker提供了两种卷&#xff1a;5.两种卷的区别6.bind mount7.Docker managed volumevolume 语法volume 操作参数 1.什么是容器数据库卷 卷 就是目录或文件&#xff0c…

对全概率公式、贝叶斯公式的理解

目录 一、全概率公式 二、贝叶斯公式 三、综合题目 一、全概率公式 定义: 在事件A发生的前提下&#xff0c;事件A又作为事件B发生的条件&#xff0c;这样两两一组的概率总和&#xff0c;就为概率论公式。题目通常问的是一整个事件的概率。别急&#xff0c;请看例题。 1.18 …

2005-2023年6月中国全球投资追踪数据(China-Global-Investment-Tracker-2023-Spring)

2005-2023年6月中国全球投资追踪数据&#xff08;China-Global-Investment-Tracker-2023-Spring&#xff09; 1、时间;2005-2023年6月 2、来源&#xff1a;American Enterprise Institute 3、指标&#xff1a;Year、Month、Investor、Quantity、in、Millions、Share、Size、…

【Python】Playwright模块进行自动化测试

playwright是由微软开发的Web UI自动化测试工具&#xff0c;支持Node.js、Python、C# 和 Java语言&#xff0c;本文将介绍Python版本的Playwright使用方法。 微软开源了一个非常强大的自动化项目叫playwright-python&#xff0c;项目地址&#xff1a;https://github.com/micros…

鸿蒙(HarmonyOS)应用开发——生命周期、渲染控制、状态管理装饰器

生命周期 任何程序都是有一定的生命周期的。生命周期是记录从产生到销毁的过程&#xff1b;如果熟悉前端vue.js的话&#xff0c;就可以很好的理解生命周期。 自定义组件生命周期 ArkTS中&#xff0c;自定义组件提供了两个生命周期函数&#xff1a;aboutToAppear() 和aboutTo…

2023年【道路运输企业安全生产管理人员】最新解析及道路运输企业安全生产管理人员复审考试

题库来源&#xff1a;安全生产模拟考试一点通公众号小程序 道路运输企业安全生产管理人员最新解析是安全生产模拟考试一点通总题库中生成的一套道路运输企业安全生产管理人员复审考试&#xff0c;安全生产模拟考试一点通上道路运输企业安全生产管理人员作业手机同步练习。2023…