两种单例模式(保证线程安全)

news2024/11/26 4:24:12

开始前,球球各位读者给个三连吧,有错误感谢指出,谢谢

单例模式也叫单个实例,也就是这个类只有且只能有一个实例对象,这样一个类就叫做“单例”;单例模式有很多种,这里只介绍“饿汉模式”和“懒汉模式”两种;

饿汉模式

唯一实例创建的时机非常早

//饿汉模式
class Singleton{
    //私有静态的实例对象,外部无法获取,随着类的记载而加载
    //类对象属性,保证只有一个
    private static Singleton singleton=new Singleton();
    //共有的静态方法,外界能够直接直接通过类名访问,获取该单例对象
    public static Singleton getSingleton(){
        return singleton;
    }
    //私有方法,确保外部无法创建该对象的实例
    private Singleton(){}
}
public class demo5 {
    public static void main(String[] args) {
        Singleton s1= Singleton.getSingleton();
        Singleton s2= Singleton.getSingleton();
        //s1,s2指向的是同一个类对象
        System.out.println(s1==s2);//true
        Singleton s3=new Singleton();//报错,外部无法再new一个实例对象
    }
}

懒汉模式

只有当该类第一次被实例化的才实例化,如果该类没有被实例化就不实例化,且要确保后续无法再次实例化,确保只有一个实例化对象;

//"懒汉模式"
class SingletonLazy{
    //首先实例对象要设置为空
    private static SingletonLazy singletonLazy=null;
    //共有的获取该类唯一实例对象的方法
    public static SingletonLazy getSingletonLazy(){
        if(singletonLazy==null){
            singletonLazy=new SingletonLazy();
        }
        return singletonLazy;
    }
    //私有的构造方法,外部无法创建该类的实例化对象
    private SingletonLazy(){}
}
public class demo6 {
    public static void main(String[] args) {
        SingletonLazy s1=SingletonLazy.getSingletonLazy();
        SingletonLazy s2=SingletonLazy.getSingletonLazy();
        //s1,s2指向的是同一个实例化对象;
        System.out.println(s1==s2);//true
        //无法创建实例化对象
        SingletonLazy s3=new SingletonLazy();
    }
}

两种模式的最大的区别就是唯一对象创建的实际不同,饿汉模式会在第一个时间创建,只要该类加载内存,唯一实例化对象就随着被创建,而懒汉模式只有当被第一次调用需要实例化对象是才会实例化唯一对象,如果不调用,就不创建;

如果程序中包含多个单例类,使用饿汉模式,刚开始就会扎堆创建很多个单例对象,可能会使得程序启动变慢,如果是懒汉模式,只有当被调用时被会实例化对象,实际是分散的,不容易感觉卡顿;

单例模式一般运用在类只需要一个实例化对象的时候,或者需要避免该类被实例化第二个对象;举个例子:你写的服务器,要从硬盘上加载100G的数据到内存中,肯定要写成一个类,封装上述加载操作,并且写一些获取/处理数据的逻辑方法,这样的类,就因该是单例的,一个实例化就要管理100g的数据,多个实例,就要加载N*100G的数据,这是没有必要,机器也是吃不消的;

 

单例模式在多线程如何保证线程安全

饿汉模式在多线程是安全的,但是懒汉模式在多线程是不安全的;

饿汉模式是安全的因为在线程还没有创建之前,唯一实例化对象就随着类的加载而创建了,所以后续无论是多少个线程的单例对象指向的都是同一个实例化对象;

懒汉模式之所以在多线程是不安全,是因为在该模式下的实例化对象是在程序运行中被创建的,这其中就有可能多个线程同时实例化对象,不安全那就要加锁,但是加锁在哪里又是一个需要考虑的问题,结合下面两个线程同时实例化对象的可能过程一起来了解一下吧

第一种情况:锁在new对象的时候

a3764114f9f844e99dbb17b427a0f315.png

第二种情况:锁在判断对象是否为空的时候

0d4a5cc1fb2840d990dcc04877ee1e07.png

第二种情况的优化

第二种情况无疑是可以在一定程度上确保线程安全的,但是针对第二种情况我们还可以再做优化,由于以上例子只是在只有两个线程访问时确保线程安全,如果有多个线程访问,这时候后续的每一个线程在获取实例化对象的,还是每次都要加锁然后再去判断对象是否为空,加锁的开销也是很大的,是一定程度上会拖慢程序的运行的,这使得加锁在一定程度上与低效挂钩,所以第二种情况的优化就是在进行一个判断singletonLazy是否为空,代码如下:

public static SingletonLazy getSingletonLazy(){
       if(singletonLazy==null){
           synchronized (singletonLazy){
               if(singletonLazy==null){
                   singletonLazy=new SingletonLazy();
               }
           }
       }
        return singletonLazy;
    }

还有一点补充,虽然这一点不会100%出现,就是编辑器对代码进行了优化,出现内存可见性的情况,即singletonLazy存在寄存器里,当线程创建了该类的唯一实例化对象时,singletonLazy并没有修改指向该对象,所以可以在上volatile关键字;

以下时懒汉模式在多线程依然保持安全完整代码:

//"懒汉模式"
class SingletonLazy{
    //首先实例对象要设置为空
    private static volatile SingletonLazy singletonLazy=null;
    //共有的获取该类唯一实例对象的方法
    public static SingletonLazy getSingletonLazy(){
       if(singletonLazy==null){
           synchronized (singletonLazy){
               if(singletonLazy==null){
                   singletonLazy=new SingletonLazy();
               }
           }
       }
        return singletonLazy;
    }
    //私有的构造方法,外部无法创建该类的实例化对象
    private SingletonLazy(){}
}

 

 

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

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

相关文章

vscode+picgo+gitee实现Markdown图床

vscode中编辑Markdown文件,复制的图片默认是保存在本地的。当文档上传csdn时,会提示图片无法识别 可以在gitee上创建图床仓库,使用picgo工具上传图片,在Markdown中插入gitee链接的方式来解决该问题。 一、 安装picgo工具 1.1 v…

1-Wire的使用

代码: ds18b20.c /*《AVR专题精选》随书例程3.通信接口使用技巧项目:1-Wire 单总线的使用文件:ds1820.c说明:DS18B20驱动文件。为了简单,没有读取芯片地址,也没有计算校验作者:邵子扬时间&…

Golang | Leetcode Golang题解之第167题两数之和II-输入有序数组

题目&#xff1a; 题解&#xff1a; func twoSum(numbers []int, target int) []int {low, high : 0, len(numbers) - 1for low < high {sum : numbers[low] numbers[high]if sum target {return []int{low 1, high 1}} else if sum < target {low} else {high--}}r…

http1.x和http2.0的一些区别

1、http2.0采用多路复用技术&#xff0c;可以同时发送多个请求或回应 2、http2.0可以由服务器主动向客户端推送数据 3、http2.0对头信息进行压缩&#xff0c;并维护一张信息表&#xff0c;生成头信息索引号&#xff0c;发送时只发送索引号

使用普通定时器产生半双工软件串口

代码&#xff1a; /*《AVR专题精选》随书例程3.通信接口使用技巧项目&#xff1a;使用普通定时器和外中断实现半双工软件串口文件&#xff1a;softuart.c说明&#xff1a;软件串口驱动文件作者&#xff1a;邵子扬时间&#xff1a;2012年12月16日*/ #include "softuart.h&…

YOLOv9基础 | 实时目标检测新SOTA,手把手带你深度解析yolov9论文!

前言:Hello大家好,我是小哥谈。YOLOv9是Chien-Yao Wang等人提出的YOLO系列的最新版本之一(截止到目前,YOLOv10已发布),于2024年2月21日发布。它是 YOLOv7的改进版本,两者均由Chien-Yao Wang及其同事开发。本节课就以YOLOv9论文为基础带大家深入解析YOLOv9算法。🌈 …

web基础学习

1、安装 1.1、创建一个 React 新项目 如果你正在学习 React 或者考虑将其应用到现有的项目中&#xff0c;你可以 利用 script 标签将 React 添加到任何 HTML 页面 来快速开启学习之旅。如果你的项目需要许多组件和许多文件&#xff0c;那就需要考虑以下方式了&#xff01; 1…

WinMerge v2 (开源的文件比较/合并工具)

前言 WinMerge 是一款运行于Windows系统下的免费开源的文件比较/合并工具&#xff0c;使用它可以非常方便地比较多个文档内容甚至是文件夹与文件夹之间的文件差异。适合程序员或者经常需要撰写文稿的朋友使用。 一、下载地址 下载链接&#xff1a;http://dygod/source 点击搜…

【干货】Android中高级开发进阶必备资料(附:PDF+视频+源码笔记)

4、数据传输与序列化 5、Java虚拟机原理 6、高效IO 设计思想解读开源框架 随着互联网企业的不断发展&#xff0c;产品项目中的模块越来越多&#xff0c;用户体验要求也越来越高&#xff0c;想实现小步快跑、快速迭代的目的越来越难&#xff0c;插件化技术应用而生。如果没有…

Python: HexBinDecOct

因为&#xff1a; f0b1001110# 十进制 int()a0*2**01*2**11*2**21*2**30*2**40*2**51*2**6print(a)# 八进制 oct()print(78/8,78%8)# 110 001 001 8 116print(1*2**00*2**10*2**2,1*2**00*2**10*2**2,0*2**01*2**11*2**2)#十六进制 hex()#0 100 1110 16 4Eprint(sixteenFoo(0*…

leetcode 二分查找·系统掌握 第一个错误版本

题意&#xff1a; 题解&#xff1a; 就是经典的~01~泛型查找&#xff0c;而且一定存在这样错误的版本所以查找不会"失败"&#xff0c;返回每次查找结果即可。 int firstBadVersion(int n) {long l1,rn,mid;while(l<r){mid(lr)>>1;if(isBadVersion(mid))r…

wordpress教程自动采集并发布工具

随着互联网的快速发展&#xff0c;越来越多的人开始关注网络赚钱。而对于许多人来说&#xff0c;拥有一个自己的个人网站是一个不错的选择。然而&#xff0c;要让自己的个人网站内容丰富多样&#xff0c;就需要不断地进行更新。那么&#xff0c;有没有一种方法可以让我们轻松地…

【大数据 复习】第7章 MapReduce(重中之重)

一、概念 1.MapReduce 设计就是“计算向数据靠拢”&#xff0c;而不是“数据向计算靠拢”&#xff0c;因为移动&#xff0c;数据需要大量的网络传输开销。 2.Hadoop MapReduce是分布式并行编程模型MapReduce的开源实现。 3.特点 &#xff08;1&#xff09;非共享式&#xff0c;…

Java学习 - 网络IP协议簇 讲解

IP协议 IP协议全称 Internet Protocol互联网互连协议 IP协议作用 实现数据在网络节点上互相传输 IP协议特点 不面向连接不保证可靠 IP协议数据报结构 组成说明版本目前有IPv4和IPv6两种版本首部长度单位4字节&#xff0c;所以首部长度最大为 15 * 4 60字节区分服务不同…

深度学习windows环境配置

1 下载CUDA和cudnn 详见文章 CUDA与CUDNN在Windows下的安装与配置&#xff08;超级详细版&#xff09;_windows cudnn安装-CSDN博客 我电脑的CUDA下载链接如下 ​​​​​https://developer.nvidia.com/cuda-12-1-0-download-archive?target_osWindows&target_archx86…

第10章 启动过程组 (制定项目章程)

第10章 启动过程组 9.1制定项目章程&#xff0c;在第三版教材第356~360页&#xff1b; 文字图片音频方式 视频12 第一个知识点&#xff1a;主要输出 1、项目章程&#xff08;重要知识点&#xff09; 项目目的 为了稳定与发展公司的客户群(抽象&#xff0c;非具体) 可测量的项目…

Pytho字符串的定义与操作

一、字符串的定义 Python 字符串是字符的序列&#xff0c;用于存储文本数据。字符串可以包括字母、数字、符号和空格。在 Python 中&#xff0c;字符串是不可变的&#xff0c;这意味着一旦创建了一个字符串&#xff0c;就不能更改其中的字符。但是&#xff0c;你可以创建新的字…

头歌资源库(15)活动安排问题

一、 问题描述 二、算法思想 这是一个经典的贪心算法问题&#xff0c;可以使用贪心算法进行求解。 首先&#xff0c;将所有活动按照结束时间从小到大进行排序。假设已经排好序的活动列表为S。 然后&#xff0c;选择第一个活动&#xff0c;将其加入到安排列表中。 接下来&…

mysql中返回日期格式带有T、Java解决返回日期格式带 ‘T‘ 问题、MySQL查询日期为什么带T、java.util.Date()类型为什么有T

文章目录 一、场景描述&#xff1a;Mysql返回日期格式带有T二、解决方法2.1、方法一&#xff1a;通过注解格式化2.2、方法二&#xff1a;通过全局配置2.3、方法三&#xff1a;查询时手动转换时间格式 三、mysql 数据库时间类型数据为什么有T3.1、什么是ISO 8601格式 四、java中…

字节跳动+京东+360+网易+腾讯,那些年我们一起踩过算法与数据结构的坑!(1)

**二面&#xff1a;**已知一棵树的由根至叶子结点按层次输入的结点序列及每个结点的度&#xff08;每层中自 左到右输入&#xff09;&#xff0c;试写出构造此树的孩子-兄弟链表的算法。 **三面主管面&#xff1a;**已知一棵二叉树的前序序列和中序序列分别存于两个一维数组中&…