八种单例模式

news2024/10/6 22:28:14

文章目录

    • 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

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

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

相关文章

Shell字符串变量

目标 能够使用字符串的3种方式 掌握Shell字符串拼接 掌握shell字符串截取的常用格式 能够定义Shell索引数组和关联数组 能够使用内置命令alias,echo,read,exit,declare操作 掌握Shell的运算符操作 Shell字符串变量 介绍 字符串(String)就是一系…

嵌入式进阶——数码管

🎬 秋野酱:《个人主页》 🔥 个人专栏:《Java专栏》《Python专栏》 ⛺️心若有所向往,何惧道阻且长 文章目录 数码管结构移位寄存器原理图移位寄存器数据流程移位寄存器控制流程移位寄存器串联实现数码管显示 数码管结构 共阴与共阳 共阳数码…

从程序被SQL注入来MyBatis 再谈 #{} 与 ${} 的区别

缘由 最近在的一个项目上面,发现有人在给我搞 SQL 注入,我真的想说我那么点资源测试用的阿里云服务器,个人估计哈,估计能抗住他的请求。狗头.png 系统上面的截图 数据库截图 说句实在的,看到这个之后我立马就是在…

架构师系列-定时任务解决方案

定时任务概述 在很多应用中我们都是需要执行一些定时任务的,比如定时发送短信,定时统计数据,在实际使用中我们使用什么定时任务框架来实现我们的业务,定时任务使用中会遇到哪些坑,如何最大化的提高定时任务的性能。 我…

设计模式深度解析:分布式与中心化,IT界两大巨头“华山论剑”

​🌈 个人主页:danci_ 🔥 系列专栏:《设计模式》《MYSQL应用》 💪🏻 制定明确可量化的目标,坚持默默的做事。 ✨IT界的两大巨头交锋✨ 👋 在IT界的广阔天地中,有两座…

【Linux 网络】网络基础(三)(网络层协议:IP 协议)

在复杂的网络环境中确定一个合适的路径。 一、TCP 与 IP 的关系 IP 层的核心作用是定位主机,具有将数据从主机 A 发送到主机 B 的能力,但是能力并不能保证一定能够做到,所以这时就需要 TCP 起作用了,TCP 可以通过超时重传、拥塞控…

DBAPI怎么进行数据格式转换

DBAPI如何进行数据格式的转换 假设现在有个API,根据学生id查询学生信息,访问API查看数据格式如下 {"data":[{"name":"Michale","phone_number":null,"id":77,"age":55}],"msg"…

JVM学习-Class文件结构②

访问标识(access_flag) 在常量池后,紧跟着访问标记,标记使用两个字节表示,用于识别一些类或接口层次的访问信息,包括这个Class是类还是接口,是否定义为public类型,是否定义为abstract类型,如果…

[vue error] vue3中使用同名简写报错 ‘v-bind‘ directives require an attribute value

错误详情 错误信息 ‘v-bind’ directives require an attribute value.eslintvue/valid-v-bind 错误原因 默认情况下,ESLint 将同名缩写视为错误。此外,Volar 扩展可能需要更新以支持 Vue 3.4 中的新语法。 解决方案 更新 Volar 扩展 安装或更新 …

Java 泛型基础

目录 1. 为什么使用泛型 2. 泛型的使用方式 2.1. 泛型类 2.2. 泛型接口 2.3. 泛型方法 3. 泛型涉及的符号 3.1. 类型通配符"?" 3.2. 占位符 T/K/V/E 3.3. 占位符T和通配符?的区别。 4. 泛型不变性 5. 泛型编译时擦除 1. 为什么使用泛型 Java 为…

Django 里的静态资源调用

静态资源:图片,CSS, JavaScript 一共有两种方法 第一种方法 在项目的文件夹里创建名为 static 文件夹 在该文件夹里,添加静态资源 在 settings.py 里添加路径 import os# Static files (CSS, JavaScript, Images) # https://docs.djan…

Git基础命令:带图整理

基础命令 Git 安装 Git下载地址 https://git-scm.com/downloads Git安装(Window/Mac) 选择不同系统安装包安装 检验是否安装成功 出现Git Bash命令行工具或Git GUI工具git --version 查看git安装版本 Git 结构 工作区(Working Direct…

干货收藏 | 掌握ChatGPT提示词的精髓:从小白到高手!!

前言 提示决定了 ChatGPT 的输出。也就是说:GPT 生成的答案质量,完全取决于你“问它”,以及“引导它”的方式,如果你能问得好,引导的好,那么它就会帮你生成让你惊喜的答案,反之则无价值&#x…

国际版Tiktok抖音运营流量实战班:账号定位/作品发布/热门推送/等等-13节

课程目录 1-tiktok账号定位 1.mp4 2-tiktok作品发布技巧 1.mp4 3-tiktok数据功能如何开通 1.mp4 4-tiktok热门视频推送机制 1.mp4 5-如何发现热门视频 1.mp4 6-如何发现热门音乐 1.mp4 7-如何寻找热门标签 1.mp4 8-如何寻找垂直热门视频 1.mp4 9-如何发现热门挑战赛 1…

从垃圾识别到收集器:详细聊聊Java的GC

个人博客 从垃圾识别到收集器:详细聊聊Java的GC | iwts’s blog 前言 聊GC,自然离不开JVM内存模型,建议先了解JVM内存模型相关内容,或者最起码了解堆相关的内容,GC主要处理的就是堆。 这里会从垃圾识别算法->GC算法->JV…

OWASP top10--SQL注入(二)

目录 06:SQL注入提交方式 6.1、get提交 6.2、post提交 6.3、cookie提交 6.4、HTTP Header头提交 07:注入攻击支持类型 7.1、union注入: 7.1.1、union操作符一般与order by语句配合使用 7.1.2、information_schema注入 7.2、基于函数…

【云原生--K8S】K8S python接口研究

文章目录 前言一、搭建ubuntu运行环境1.运行ubuntu容器2.拷贝kubeconfig文件二、python程序获取k8s信息1.获取node信息2.获取svc信息3.常用kubernetes API总结前言 在前面的文章中我们都是通过kubectl命令行来访问操作K8S,但是在实际应用中可能需要提供更方便操作的图形化界面…

数据结构和算法|排序算法系列(二)|冒泡排序

首先需要你对排序算法的评价维度和一个理想排序算法应该是什么样的有一个基本的认知: 《Hello算法之排序算法》 主要内容来自:Hello算法11.3 冒泡排序 我觉得冒泡排序非常有意思,也非常简单,就是不停地交换相邻的元素即可&#…

c语言IO

前言 老是忘记c语言IO操作,故写个文章记录一下 打开文件 fopen FILE *fopen(const char *path, const char *mode);mode 返回值 如果文件成功打开,fopen 返回一个指向 FILE 结构的指针。如果文件打开失败(例如,因为文件不存…

SERVER ——查询(二)

目录 5. top 6. null 7. order by 8. 模糊查询: 9. 聚合函数 5. top top查询:查询表的前几行;下面是代码演示: --top(前面的几个记录) select top 2 * from emp; --查询表的前两列 select top 20 percent *…