八种单例模式

文章目录

    • 1.单例模式基本介绍
        • 1.介绍
        • 2.单例模式八种方式
    • 2.饿汉式(静态常量,推荐)
        • 1.基本步骤
          • 1.构造器私有化(防止new)
          • 2.类的内部创建对象
          • 3.向外暴露一个静态的公共方法
        • 2.代码实现
        • 3.优缺点分析
    • 3.饿汉式(静态代码块,推荐)
        • 1.代码实现
        • 2.优缺点分析
    • 4.懒汉式(线程不安全,实际开发不要使用)
        • 1.代码实现
        • 2.优缺点分析
    • 5.懒汉式(线程安全,同步方法,实际开发不推荐使用)
        • 1.代码实现
        • 2.优缺点分析
    • 6.懒汉式(线程安全,同步代码块,解决不了同步问题)
        • 1.介绍
        • 2.优缺点分析
    • 7.双重检查(推荐使用)
        • 1.代码实现
        • 2.关于volatile关键字
          • 使用 `volatile` 的常见场景
          • 复合操作的限制
          • 总结
        • 3.优点分析
    • 8.静态内部类实现(推荐使用)
        • 1.前置知识
          • 1.外部类被装载,静态内部类不会被装载
          • 2.当调用静态内部类的属性时,静态内部类会被装载且只会被装载一次
          • 3.在类装载的时候是线程安全的
        • 2.代码实现
        • 3.优点分析
    • 9.枚举实现(推荐使用)
        • 1.代码实现
        • 2.优缺点分析
    • 10.单例模式总结
        • 1.Runtime就使用了饿汉式的单例模式
        • 2.注意事项和细节

1.单例模式基本介绍

1.介绍

image-20240526192046799

2.单例模式八种方式

image-20240526192132094

2.饿汉式(静态常量,推荐)

1.基本步骤
1.构造器私有化(防止new)
2.类的内部创建对象
3.向外暴露一个静态的公共方法
2.代码实现
package com.sun.type1;

/**
 * Description: 单例模式饿汉式(静态变量)
 * @Author sun
 * @Create 2024/5/26 19:28
 * @Version 1.0
 */
public class Singleton01 {
    public static void main(String[] args) {
        // 通过公有的静态方法,获取实例
        Singleton instance = Singleton.getInstance();
        Singleton instance1 = Singleton.getInstance();
        System.out.println(instance1 == instance); // 返回的是true
    }
}

class Singleton {

    // 1.构造器私有化
    private Singleton() {}

    // 2.本类的内部创建对象实例
    private final static Singleton instance = new Singleton();

    // 3.暴露一个公有的静态方法,返回实例对象
    public static Singleton getInstance() {
        return instance;
    }
}

image-20240526194327006

3.优缺点分析

image-20240526194535444

3.饿汉式(静态代码块,推荐)

1.代码实现
package com.sun.type1;

/**
 * Description: 单例模式饿汉式(静态代码块)
 * @Author sun
 * @Create 2024/5/26 19:49
 * @Version 1.0
 */
public class Singleton02Test {
    public static void main(String[] args) {
        // 通过公有的静态方法,获取实例
        Singleton02 instance = Singleton02.getInstance();
        Singleton02 instance1 = Singleton02.getInstance();
        System.out.println(instance1 == instance); // 返回的是true
    }
}

class Singleton02 {

    // 1.构造器私有化
    private Singleton02() {}

    // 2.本类的内部有一个静态属性
    private static Singleton02 instance;

    // 3.使用静态代码块为静态属性初始化
    static {
        instance = new Singleton02();
    }

    // 4.暴露一个公有的静态方法,返回实例对象
    public static Singleton02 getInstance() {
        return instance;
    }
}

2.优缺点分析

image-20240526200000545

4.懒汉式(线程不安全,实际开发不要使用)

1.代码实现
package com.sun.type3;

/**
 * Description: 单例模式懒汉式(线程不安全)
 * @Author sun
 * @Create 2024/5/26 20:02
 * @Version 1.0
 */
public class Singleton03Test {
    public static void main(String[] args) {
        Singleton instance = Singleton.getInstance();
        Singleton instance1 = Singleton.getInstance();
        System.out.println(instance1 == instance);
    }
}

class Singleton {

    // 1.构造器私有化
    private Singleton() {}

    // 2.静态属性,存放本类对象
    private static Singleton instance;

    // 3.静态的公有方法,当使用到该方法时才会去创建instance
    public static Singleton getInstance() {
        // 当静态属性没有被赋值的时候再去创建
        if (instance == null) {
            instance = new Singleton();
        }
        // 如果这个静态属性被初始化过了,就直接返回,保证是单例的
        return instance;
    }
}


2.优缺点分析

image-20240526201118772

5.懒汉式(线程安全,同步方法,实际开发不推荐使用)

1.代码实现
package com.sun.type4;

/**
 * Description: 单例模式懒汉式(线程安全,同步方法)
 * @Author sun
 * @Create 2024/5/26 20:15
 * @Version 1.0
 */
public class Singleton04Test {
    public static void main(String[] args) {
        Singleton instance = Singleton.getInstance();
        Singleton instance1 = Singleton.getInstance();
        System.out.println(instance1 == instance);
    }
}

class Singleton {

    // 1.构造器私有化
    private Singleton() {}

    // 2.静态属性,存放本类对象
    private static Singleton instance;

    // 3.静态的公有方法,当使用到该方法时才会去创建instance,使用synchronized实现线程安全
    public static synchronized Singleton getInstance() {
        // 当静态属性没有被赋值的时候再去创建
        if (instance == null) {
            instance = new Singleton();
        }
        // 如果这个静态属性被初始化过了,就直接返回,保证是单例的
        return instance;
    }
}


2.优缺点分析

image-20240526202034683

6.懒汉式(线程安全,同步代码块,解决不了同步问题)

1.介绍

image-20240526202352115

2.优缺点分析

image-20240526202512379

7.双重检查(推荐使用)

1.代码实现
package com.sun.type6;

/**
 * Description: 双重检查
 * @Author sun
 * @Create 2024/5/26 20:15
 * @Version 1.0
 */
public class Singleton06Test {
    public static void main(String[] args) {
        Singleton instance = Singleton.getInstance();
        Singleton instance1 = Singleton.getInstance();
        System.out.println(instance1 == instance);
    }
}

class Singleton {

    // 1.构造器私有化
    private Singleton() {}

    // 2.静态属性,存放本类对象,使用volatile防止指令重排序
    private static volatile Singleton instance;

    // 3.静态的公有方法,当使用到该方法时才会去创建instance
    public static Singleton getInstance() {
        // 先检查一次静态属性是否为空,这样可以过滤掉一部分
        if (instance == null) {
            // 如果为空,则进入同步代码块创建对象,由于还是会有多个线程进来,所以在内部需要二次检查
            synchronized (Singleton.class) {
                // 在此完成双重检查
                if (instance == null) {
                    instance = new Singleton();
                }
            }
        }
        // 如果这个静态属性被初始化过了,就直接返回,保证是单例的
        return instance;
    }
}


2.关于volatile关键字
使用 volatile 的常见场景
  1. 直接赋值

    • 设置标志位,如控制线程的开始或停止。
    • 单个配置值的更新,这个值被多个线程读取。

    例如,一个线程控制开关:

    public class TaskRunner implements Runnable {
        private volatile boolean stop = false;
    
        public void run() {
            while (!stop) {
                // 执行任务
            }
        }
    
        public void stop() {
            stop = true;
        }
    }
    
  2. 单一读取操作

    • 在这种情况下,volatile 保证了每次读取操作都从主内存中获取最新值,而不是从线程的本地缓存。
复合操作的限制

如果操作涉及到对变量的读取、修改和写回(如 a++),则 volatile 不能保证操作的原子性。这类操作需要使用 synchronizedjava.util.concurrent.atomic 包中的原子类。例如,对一个计数器进行增加操作:

public class Counter {
    private volatile int count = 0; // 这是错误的做法,对于 count++ 操作

    public void increment() {
        count++; // 这不是线程安全的
    }
}

在上面的代码中,count++ 操作实际上是三个步骤:读取 count 的当前值,增加 1,然后写回新值。这三个步骤不是原子操作,因此即使 count 被声明为 volatile,在多线程环境中也可能导致不正确的结果。

总结

总之,当你的需求仅限于确保多线程之间对单个变量直接赋值操作的可见性和有序性时,使用 volatile 是合适的。然而,对于需要多个步骤或基于当前值的修改的操作,你需要使用更强的同步机制。

3.优点分析

image-20240526204442702

8.静态内部类实现(推荐使用)

1.前置知识
1.外部类被装载,静态内部类不会被装载
2.当调用静态内部类的属性时,静态内部类会被装载且只会被装载一次
3.在类装载的时候是线程安全的
2.代码实现
package com.sun.type7;

/**
 * Description: 单例模式(静态内部类实现)
 * @Author sun
 * @Create 2024/5/26 20:51
 * @Version 1.0
 */
public class Singleton07Test {
    public static void main(String[] args) {
        Singleton instance = Singleton.getInstance();
        Singleton instance1 = Singleton.getInstance();
        System.out.println(instance1 == instance);
    }
}

class Singleton {

    // 1.构造器私有化
    private Singleton() {}

    // 2.一个静态内部类,该类有一个静态属性,存放外部类的对象
    private static class SingletonInstance {
        public static final Singleton INSTANCE = new Singleton();
    }

    // 3.静态的公有方法,当要使用时调用内部类的静态属性,此时静态内部类会被装载,且只会被装载一次
    public static Singleton getInstance() {
        return SingletonInstance.INSTANCE;
    }
}
3.优点分析

image-20240526210255702

9.枚举实现(推荐使用)

1.代码实现
package com.sun.type8;

/**
 * Description: 单例模式(静态内部类实现)
 * @Author sun
 * @Create 2024/5/26 20:51
 * @Version 1.0
 */
public class Singleton08Test {
    public static void main(String[] args) {
        Singleton instance = Singleton.INSTANCE;
        Singleton instance2 = Singleton.INSTANCE;
        System.out.println(instance2 == instance);
    }
}

enum Singleton {
    // 这个就相当于调用无参构造创建了这个类的一个对象,可以实现单例模式
    INSTANCE;
}
2.优缺点分析

image-20240526210742011

10.单例模式总结

1.Runtime就使用了饿汉式的单例模式

image-20240526211234759

2.注意事项和细节

image-20240526211508150

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

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

相关文章

深入浅出MySQL事务实现底层原理

重要概念 事务的ACID 原子性(Atomicity):即不可分割性,事务中的操作要么全不做,要么全做一致性(Consistency):一个事务在执行前后,数据库都必须处于正确的状态&#xf…

XSS+CSRF攻击

一、前言 在DVWA靶场的XSS攻击下结合CSRF攻击完成修改密码 也就是在具有XSS漏洞的情况下实施CSRF攻击 二、实验 环境配置与上一篇博客一致,有兴趣可以参考CSRF跨站请求伪造实战-CSDN博客 首先登录DVWA,打开XSS模块 name随便输入,message…

Linux服务的简介与分类

服务的简介与分类 服务的分类 查询已安装的服务和区分服务 #列出所有rpm包默认安装服务的自启动状态 [rootlocalhost ~]# chkconfig --list atd atd 0:关闭 1:关闭 2:关闭 3:启用 4:启用 5:启用 6:关闭 [rootlocalhost ~]# chkconfig --list sshd sshd …

MDK安装

MDK安装 1 MDK的差异2 切换MDK3 安装芯片支持包注意点 1 MDK的差异 不同版本MDK有略微的差别,比如:MDK536.EXE,支持版本5的交叉编译链。如下图所示: 而MDK539.EXE不支持版本5的交叉编译链,所以工作的时候&#xff0c…

[JDK工具-6] jmap java内存映射工具

文章目录 1. 介绍2. 主要选项3. 生成java堆转储快照 jmap -dump4. 显示堆详细信息 jmap -heap pid5. 显示堆中对象统计信息 jmap -histo pid jmap(Memory Map for Java) 1. 介绍 位置:jdk\bin 作用: jdk安装后会自带一些小工具,jmap命令(Mem…

vue3模板语法以及attribute

模板语法​ Vue 使用一种基于 HTML 的模板语法,使我们能够声明式地将其组件实例的数据绑定到呈现的 DOM 上。所有的 Vue 模板都是语法层面合法的 HTML,可以被符合规范的浏览器和 HTML 解析器解析。 在底层机制中,Vue 会将模板编译成高度优化…

Python Beautiful Soup 使用详解

大家好,在网络爬虫和数据抓取的领域中,Beautiful Soup 是一个备受推崇的 Python 库,它提供了强大而灵活的工具,帮助开发者轻松地解析 HTML 和 XML 文档,并从中提取所需的数据。本文将深入探讨 Beautiful Soup 的使用方…

解决:LVGL+GUI Guider 1.7.2运行一段时间就会卡死死机,内存泄露溢出的问题

概括: 我在使用NXP官方GUI Guider生成的代码出现了内存泄漏的问题。但我遇到的并不是像其他人所说的style的问题,如下链接。而是因为在页面渲染之前就使用了该页面内的组件,内存就会不断增加。 LVGL 死机 内存泄漏_lvgl 内存溢出-CSDN博客 运…

springboot整合kkFileView部署,前端使用

前言: 官方文档:https://kkfileview.keking.cn/zh-cn/docs/production.html docker方式或加入星球获取发行包直接获取启动,无需以下步骤: 拉取镜像# 网络环境方便访问docker中央仓库 docker pull keking/kkfileview:4.1.0# 网…

明星IP切片带货爆单营,0基础搞定IP切片带货短视频(69节课)

把握带货趋势,了解切片流程,剪辑带货创收营 课程目录: 01第一章实操链路-第一节IP选择.mp4 02第一章实操链路-第二节账号准备.mp4 03第一章实操链路-第四节开通权限.mp4 04第一章实操链路-第五节货品准备.mp4 05第一章实操链路-第六节素…

Java进阶学习笔记26——包装类

包装类: 包装类就是把基本类型的数据包装成对象。 看下API文档: deprecated:极力反对、不赞成的意思。 marked for removal:标识为去除的意思。 自动装箱:基本数据类型可以自动转换成包装类。 自动拆箱:…

编写子函数+最大公约数和最小公倍数

目录 计算级数和 判断并找出非素数 主函数操作流程 求最大公约数和最小公倍数 编写子函数,该函数的功能是是计算下列级数和,并将和值返回主调函数输出。数据由主函数输入。 fun 函数 sum 函数 main 函数 注意事项 编写函数,该函数的…

Jenkins pipeline发布前端项目

说明:第一次使用jenkins生成pipeline片段,做个记录... 1.全局工具配置添加自定义node版本 2.系统管理添加前端应用部署服务器 2.1 点击高级选择账号密码验证方式,添加服务器的用户和密码 3.系统管理--凭据--系统--全局凭据--添加自己的git凭据…

总是等不是办法,向媒体投稿你得学会用新方法

初入信息宣传领域,我怀揣着对文字的热爱与传播价值的热情,肩负起了单位活动的宣传报道重任。那时的我,满脑子都是传统的投稿思维:精心撰写每一篇稿件,然后逐一搜寻各大媒体的投稿邮箱,一封封邮件满怀期待地发出,像播撒希望的种子,渴望在广袤的媒体土壤中生根发芽。然而,理想很丰…

Hibernate

主流ORM框架Object Relation Mapping对象关系映射,将面向对象映射成面向关系。 如何使用 1、导入相关依赖 2、创建Hibernate配置文件 3、创建实体类 4、创建实体类-关系映射文件 5、调用Hibernate API完成操作 具体操作 1、创建 Maven工程,在pom.xml中导…

基于PID的单片机温度控制系统设计

基于PID的温度控制系统设计 摘要 温度是工业上最基本的参数,与人们的生活紧密相关,实时测量温度在工业生产中越来越受到重视,离不开温度测量所带来的好处,因此研究控制和测量温度具有及其重要的意义。 本设计介绍了以AT89C52单片…

Go GORM介绍

GORM 是一个功能强大的 Go 语言 ORM(对象关系映射)库,它提供了一种方便的方式来与 SQL 数据库进行交互,而不需要编写大量的 SQL 代码。 GORM的关键特性 全功能的ORM:支持几乎所有的ORM功能,包括模型定义、基…

揭秘C++ String容器:字符串操作的艺术

目录 ​编辑 引言 一、初识std::string:构造与初始化 二、字符串的操纵艺术:拼接、查找与替换 三、访问与遍历:字符的细腻触感 四、大小与容量:动态调整的智慧 五、进阶功能:探索更多可能 结语 引言 在C标准库…

vue3+electron+typescript 项目安装、打包、多平台踩坑记录

环境说明 这里的测试如果没有其他特别说明的,就是在win10/i7环境,64位 创建项目 vite官方是直接支持创建electron项目的,所以,这里就简单很多了。我们已经不需要向开始那样自己去慢慢搭建 yarn create vite这里使用yarn创建&a…

特殊变量笔记3

输入一个错误命令, 在输出$? 特殊变量:$$ 语法 $$含义 用于获取当前Shell环境的进程ID号 演示 查看当前Shell环境进程编号 ps -aux|grep bash输出 $$ 显示当前shell环境进程编号 小结 常用的特殊符号变量如下 特殊变量含义$n获取输入参数的$0, 获取当前She…