【JAVAEE】单例模式的介绍及实现(懒汉模式和饿汉模式)

news2024/11/25 14:46:15

目录

1.单例模式

1.1概念

1.2单例模式的实现方式

怎么去设计一个单例

饿汉模式

懒汉模式

懒汉模式-多线程版 

解决懒汉模式多线程不安全问题-synchronized

解决懒汉模式多线程不安全问题-volatile


1.单例模式

1.1概念

单例是一种设计模式

啥是设计模式 ?
设计模式好比象棋中的 " 棋谱 ". 红方当头炮 , 黑方马来跳 . 针对红方的一些走法 , 黑方应招的时候有一些固定的套路 . 按照套路来走局势就不会吃亏 .
软件开发中也有很多常见的 " 问题场景 ". 针对这些问题场景 , 大佬们总结出了一些固定的套路 . 按照这个套路来实现代码 , 也不会吃亏 .

单例在全局范围内只有一个实例对象。

单例模式能保证某个类在程序中只存在唯一一份实例,而不会创建多个实例。

例如:JDBC中的DateSourse实例就只需要一个。

1.2单例模式的实现方式

怎么去设计一个单例

1.口头约定

对外提供一个方法,要求大家使用这个对象的时候,通过这个方法来获取。(不太靠谱,一般不采用)

2.使用变成语言本身的特性来处理

首先分析一下在Java中哪些对象是全局唯一的:

①类对象  .class

②用static修饰的变量(static修饰的变量是类的成员变量,所有实例对象,访问的都是同一个成员变量)

通过类对象和static配合可以实现单例的目的。

示例(实现单例模式):

用static修饰变量,变量就是自己,并且赋初始值。再提供一个对外获取实例对象的方法。

    private static Singleton instance=new Singleton();

    public Singleton getInstance() {
        return instance;
    }

验证单例是否正确

public static void main(String[] args) {
        //验证单例是否正确
        Singleton singleton01=new Singleton();
        Singleton singleton02=new Singleton();
        Singleton singleton03=new Singleton();
        //分别打印
        System.out.println(singleton01.getInstance());
        System.out.println(singleton02.getInstance());
        System.out.println(singleton03.getInstance());
    }

结果:

 既然是单例,那么通过new的方法去获取对象是有歧义的,不能让外部去new这个对象。

将构造方法私有化,通过静态方法调用,获取单例:

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

    //构造方法私有化
    private Singleton(){};

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

用类名去调用:

        Singleton singleton01=Singleton.getInstance();
        Singleton singleton02=Singleton.getInstance();
        Singleton singleton03=Singleton.getInstance();
        System.out.println(singleton01);
        System.out.println(singleton02);
        System.out.println(singleton03);

结果依旧正确:

 

饿汉模式

类一加载就完成初始化的方式称为饿汉模式

特点:书写简单,不容易出错。

上面那个例子就是饿汉模式:

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

    //构造方法私有化
    private Singleton(){};

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

懒汉模式

彼岸程序启动的时候浪费过多的系统资源,当程序使用这个对象时再对他进行初始化。即类加载的时候不创建实例,第一次使用的时候才创建实例的方式称为懒汉模式

示例:

public class SingletonLazy {
    //定义一个类成员变量
    private static  SingletonLazy instance=null;

    public static SingletonLazy getInstance(){
        if(instance==null){
            instance=new SingletonLazy();
        }
        return instance;
    }
}

运行一下,结果如下:

public static void main(String[] args) {
        SingletonLazy singleton01=SingletonLazy.getInstance();
        SingletonLazy singleton02=SingletonLazy.getInstance();
        SingletonLazy singleton03=SingletonLazy.getInstance();
        System.out.println(singleton01);
        System.out.println(singleton02);
        System.out.println(singleton03);
    }

懒汉模式-多线程版 

懒汉模式的实现其实是线程不安全的。

例如(在多线程环境下获取单例对象):

public static void main(String[] args) {
        //创建多个线程,并获取单例对象
        for (int i = 0; i < 10; i++) {
            Thread thread=new Thread(()->{
                //获取单例对象,并打印
                SingletonLazy instance=SingletonLazy.getInstance();
                System.out.println(instance);
            });
            //启动线程
            thread.start();
        }
    }

结果:

发现获取到了不同的对象,并不符合我们的预期,也就是说出现了线程不安全的现象。

那具体是什么原因导致的线程不安全呢?

t1先LOAD,然后t2执行完所有指令后,t1再继续执行。如上图所示,进行了两次初始化。所以,再多线程环境下,多个线程又可能创建多个实例。

解决懒汉模式多线程不安全问题-synchronized

①可以给getInstance方法加锁。

②将synchronized的范围指定为整个初始化过程,与在方法中加锁是等价的

synchronized (SingletonLazy.class) {
            if (instance == null) {
                instance = new SingletonLazy();
            }
        }

 得到的结果目前看来是符合预期的。

但是有一个非常严重的问题:

每一次调用getInstance()方法的时候,都需要进行锁竞争,再进行判断,而锁竞争是非常耗费系统资源的。其实,synchronized代码块再整个程序运行过程中,只需要执行一次就够了。

用户态:Java层面,在JVM中执行的代码

内核态:执行的是CPU指令

也就是说加了synchronized参与锁竞争之后就从应用层面进入到了系统层面。

为了避免过度锁竞争,这里可以加入一层判断

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

双重检查,避免了过度耗费系统资源。

执行一下,结果正确:

 public static void main(String[] args) {
        // 创建多个线程,并获取单例对象
        for (int i = 0; i < 10; i++) {
            Thread thread = new Thread(() -> {
                // 获取单例对象,并打印
                SingletonDCL instance = SingletonDCL.getInstance();
                System.out.println(instance);
            });
            // 启动线程
            thread.start();
        }

    }

解决懒汉模式多线程不安全问题-volatile

synchronized关键字解决了原子性,内存可见性问题。

volatile关键字解决有序性问题。

初始化过程,并不是一条指令。在整个初始化过程中,经历如下阶段:

1.在内存中开辟一片空间

2.初始化对象的属性(数据)

3.把内存中的地址,赋给instance变量

程序在正在执行的过程中就是按1,2,3这个顺序执行的。1与3是强相关的执行过程,2是一个单独的过程,所以编译或者CPU就有可能进行指令重排序,使程序执行过程变1,3,2.。如果出现这种顺序,那么其它线程就又肯拿到一个创建了一半的对象,导致了线程不安全。

这个时候就需要用volatile修饰变量,禁止指令重排序。

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

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

相关文章

机器学习——弹性网估计

机器学习——弹性网估计 文章目录 机器学习——弹性网估计[toc]1 模型介绍2 模型设定3 弹性网估计 1 模型介绍 弹性网估计属于惩罚回归&#xff0c;常见的惩罚回归包括岭回归(ridge)、套索回归(lasso)和弹性网(elasticnet)回归等。 岭回归用于缓解高维数据可能的多重共线性问…

aws exam

Route 53 Route 53 是AWS的一个服务&#xff0c;它的主要功能如下&#xff0c;下面会一一介绍每个功能 Domain registration&#xff08;域名注册&#xff09;DNS management&#xff08;DNS管理&#xff09;Health check&#xff08;健康检查&#xff09;Routing polices&am…

K8S系列之NetworkPolicy

什么是NetworkPolicy IP 地址或端口层面&#xff08;OSI 第 3 层或第 4 层&#xff09;控制网络流量&#xff0c; 则你可以考虑为集群中特定应用使用 Kubernetes 网络策略&#xff08;NetworkPolicy&#xff09;。 NetworkPolicy 是一种以应用为中心的结构&#xff0c;允许你设…

浅析EasyCVR视频汇聚技术在城市智慧文旅数智平台中的应用意义

一、背景分析 根据文化和旅游部4月21日公布的2023年一季度国内旅游数据情况抽样调查统计结果显示&#xff0c;2023年一季度&#xff0c;国内旅游总人次12.16亿&#xff0c;比上年同期增加3.86亿&#xff0c;同比增长46.5%。其中&#xff0c;城镇居民国内旅游人次9.44亿&#x…

消息和消息队列、以及作用场景(一)

“消息”是在两台计算机间传送的数据单位。消息可以非常简单&#xff0c;例如只包含文本字符串&#xff1b;也可以更复杂&#xff0c;可能包含嵌入对象。 “消息队列”是在消息的传输过程中保存消息的容器。 目前的消息队列有很多&#xff0c;例如&#xff1a;Kafka、RabbitMQ…

包装三年经验拿21K,试用期没过完就被裁了....

最近翻了一些网站的招聘信息&#xff0c;把一线大厂和大型互联网公司看了个遍&#xff0c;发现市场还是挺火热的&#xff0c;虽说铜三铁四&#xff0c;但是软件测试岗位并没有削减多少&#xff0c;建议大家有空还是多关注和多投简历&#xff0c;不要闭门造车&#xff0c;错过好…

Redis命令详解

Redis是一个高性能的内存键值数据库&#xff0c;它支持多种数据结构&#xff0c;包括字符串、哈希、列表、集合、有序集合等。Redis通过提供一组命令来实现对数据的操作&#xff0c;这些命令可以通过Redis客户端发送给Redis服务器&#xff0c;从而对数据库进行操作。 Redis的一…

阿里云刘伟光:2 万字解读金融级云原生

作者&#xff1a;刘伟光&#xff0c;阿里云智能新金融&互联网行业总裁、中国金融四十人论坛常务理事&#xff0c;毕业于清华大学电子工程系 01 前言 2015年云原生理念提出的时候&#xff0c;彼时全球金融百年发展形成的信息化到数字化的背后&#xff0c;金融级的技术服务…

好用工具第1期:手机电脑同屏QtScrcpy

QtScrcpy 可以通过 USB / 网络连接Android设备&#xff0c;并进行显示和控制。无需root权限。 同时支持 GNU/Linux &#xff0c;Windows 和 MacOS 三大主流桌面平台。 QtScrcpy 是一个开源项目, 项目地址是: https://github.com/barry-ran/QtScrcpy 它专注于: 精致 (仅显示设…

Java 责任链模式详解

责任链模式&#xff08;Chain of Responsibility Pattern&#xff09;是一种行为型设计模式&#xff0c;它用于将请求的发送者和接收者解耦&#xff0c;使得多个对象都有机会处理这个请求。在责任链模式中&#xff0c;有一个请求处理链条&#xff0c;每个处理请求的对象都是一个…

mysql数据库基础知识,mysql数据库简介(一看就懂,一学就会)

目录 一、MySQL学习路线二、MySQL常见操作1、查看所有数据库show databases。2、MySQL 创建数据库3、删除数据库4、选择数据库use databasename5、查看该数据库下所有表show tables6、创建数据库表7、删除数据库 三、增删改查1、插入数据2、查询数据3、where子句4、更新语句5、…

微前端应用(qiankun+umi+antd)

1.微前端介绍以应用选型 1.1什么是微前端? 微前端是一种前端架构模式&#xff0c;它将前端应用程序拆分成多个小型的、独立开发、独立部署的子应用&#xff0c;然后将这些子应用组合成一个大型的、复杂的前端应用。每个子应用都有自己的技术栈、独立的代码库、独立的开发、测…

Linux快捷命令

目录 一、快捷排序——sort 常用选项&#xff1a; 示例 二、快捷去重——uniq 常用选项&#xff1a; 示例&#xff1a; ​编辑 ​编辑 ​编辑 三、快捷替换——tr 用于windows的编写的脚本格式转换为Linux格 方法一&#xff1a; 方法二&#xff1a; 四、快速裁…

JAVA double精度丢失问题

double类型精度丢失问题&#xff1a; 0.1*0.1使用计算器计算是0.01&#xff0c;代码里却是0.010000000000000002 public class HelloWorld {public static void main(String []args) {double number1 0.1;double number2 0.1;double result number1 * number2 ;System.o…

CSP-S 2022 提高级 第一轮 阅读程序(1) 第16-21题

【题目】 CSP-S 2022 提高级 第一轮 阅读程序&#xff08;1&#xff09; 第16-21题 01 #include <iostream> 02 #include <string> 03 #include <vector> 04 05 using namespace std; 06 07 int f(const string &s, const string &t) 08 { …

关于cartographer建立正确关系树的理解

正确的TF关系map----odom----base_link----laser base_link是固定在机器人本体上的坐标系&#xff0c;通常选择飞控 其中map–odom 的链接是由cartographer中lua文件配置完成的 map_frame "map", tracking_frame "base_link", published_frame "b…

Ubuntu 20.04 安装 mysql8 并配置远程访问

文章目录 一、使用 apt-get 安装 mysql 服务二、初始化 mysql 数据库管理员用户密码三、配置远程访问 一、使用 apt-get 安装 mysql 服务 # 更新软件源 apt-get install update# 安装mysql服务 apt-get install mysql-server# 使用mysqladmin工具查看mysql版本 mysqladmin --v…

一文解析Linux进程的睡眠和唤醒

Linux进程的睡眠和唤醒 在Linux中&#xff0c;仅等待CPU时间的进程称为就绪进程&#xff0c;它们被放置在一个运行队列中&#xff0c;一个就绪进程的状 态标志位为 TASK_RUNNING。一旦一个运行中的进程时间片用完&#xff0c; Linux 内核的调度器会剥夺这个进程对CPU的控制权&…

燃气巡检二维码

对燃气公司的输气管道和阀井等设施的巡检工作的管理目标是能降低成本、提高工作效率以及管理水平。但用纸质记录的方式进行燃气设备巡检有以下缺点&#xff1a; 1、难保证巡检真实性 无法客观、方便地掌握巡检人员巡检的到位情况&#xff0c;因而无法有效地保证巡检工作人员按计…

软件兼容性测试如何进行?怎么选择靠谱的软件检测公司?

软件兼容性测试是一项非常重要的工作&#xff0c;能够确保在不同的操作系统、设备、浏览器以及其他软件环境下&#xff0c;软件应用都能够正常运行。 一、软件兼容性测试如何进行? 确定测试的环境&#xff0c;包括操作系统、设备、浏览器等&#xff0c;并建立测试用例和测试…