设计模式-单例模式

1、概念

创建型模式

确保一个类在任何情况下都绝对只有一个实例,并且提供一个全局访问点。

2、模式

1)饿汉式单例模式

在类加载的时候就立即初始化,并且创建单例对象。

绝对线程安全,在线程还没出现以前就实例化了,不可能存在访问安全问题

//标准代码
public class HungrySingleton {private static final HungrySingleton hungrySingleton = new HungrySingleton();private HungrySingleton() {}public static HungrySingleton getInstance() {return hungrySingleton;}
}//静态代码块
public class HungryStaticSingleton {private static final HungryStaticSingleton hungrySingleton;static {hungrySingleton = new HungryStaticSingleton();}private HungryStaticSingleton() {}public static HungryStaticSingleton getInstance() {return hungrySingleton;}
}

适用:

单例对象较少的情况,可以保证线程安全,执行效率高

缺点:

所有对象类加载的时候就实例化,如果系统中有大批量的单例对象存在,那么系统初始化导致大量的内存浪费

2)懒汉式单例

单例对象要在被使用的时候才会初始化

public class LazySimpleSingleton {private LazySimpleSingleton() {}private static LazySimpleSingleton lazy = null;public static LazySimpleSingleton getInstance() {if(lazy == null){lazy = new LazySimpleSingleton();}return lazy;}}

导致一个新的问题,如果在多线程环境下,会出现线程安全问题

public class ExectorThread implements Runnable {@Overridepublic void run() {LazySimpleSingleton instance = LazySimpleSingleton.getInstance();System.out.println(Thread.currentThread().getName() + ":" + instance);}
}public class Test {public static void main(String[] args) {Thread thread = new Thread(new ExectorThread());Thread thread1 = new Thread(new ExectorThread());thread.start();thread1.start();System.out.println("END");}
}

DEBUG多线程环境下,可以看到LazySimpleSingleton被实例化了两次,可能会出现打印结果一致的情况,实际也是后面执行的线程覆盖了前者的结果。

public class LazySingleton {private LazySingleton() {}private static LazySingleton lazy = null;public synchronized static LazySingleton getInstance() {if(lazy == null){lazy = new LazySingleton();}return lazy;}}

加上synchronized关键字加锁,如果在线程数量较多的情况下,如果CPU分配压力上升,会导致大批线程阻塞,导致程序性能下降。

public class LazyDoubleCheckSingleton {private volatile static LazyDoubleCheckSingleton lazy = null;private LazyDoubleCheckSingleton() {}public static LazyDoubleCheckSingleton getInstance() {if (lazy == null) {synchronized (LazyDoubleCheckSingleton.class) {if (lazy == null) {lazy = new LazyDoubleCheckSingleton();}}}return lazy;}}

第一个线程调用getInstance()时,第二个线程也可以调用。当一个线程执行 synchronized 时会上锁,第二个线程会变成MONITOR状态,出现阻塞,阻塞并不是基于整个LazyDoubleCheckSingleton类的上锁,而是在getInstance()内部的阻塞,只要逻辑不是很负责,调用者感受不到阻塞的时间差。

 

3)注册式单例模式(登记式单例模式

将每一个实例都登记到某一个地方,使用唯一的标识获取实例。

public enum EnumSingleton {INSTANCE;private Object data;public void setData(Object data) {this.data = data;}public Object getData() {return data;}public static EnumSingleton getInstance(){return INSTANCE;}
}
public static void main(String[] args) {try {EnumSingleton instance = EnumSingleton.getInstance();instance.setData(new Object());FileOutputStream fos = new FileOutputStream("EnumSingleton.obj");ObjectOutputStream oos = new ObjectOutputStream(fos);oos.writeObject(instance);oos.flush();oos.close();FileInputStream fis = new FileInputStream("EnumSingleton.obj");ObjectInputStream ois = new ObjectInputStream(fis);EnumSingleton enumSingleton = null;enumSingleton = (EnumSingleton) ois.readObject();ois.close();System.out.println(instance.getData());System.out.println(enumSingleton.getData());System.out.println(enumSingleton.getData() == instance.getData());} catch (Exception e) {e.printStackTrace();}}

枚举式单例模式在静态代码块中就给INSTANCE进行赋值,是饿汉式单例模式的体现。

看源代码:

  •  readEnum():,通过类名和类对象类找到一个唯一的枚举对象。枚举对象不可能被类加载器加载多次。

再看反射是否能破坏单例:

        try{Class<EnumSingleton> enumSingletonClass = EnumSingleton.class;Constructor c = enumSingletonClass.getDeclaredConstructor();c.newInstance();}catch (Exception e){e.printStackTrace();}

 没有找到无参的构造方法

修改代码

 不能用发射来创建枚举类型

在源码中,newInstance()做了强制性的判断

 

4)容器式单例

public class ContainerSingleton {private ContainerSingleton() {}private static Map<String, Object> ioc = new ConcurrentHashMap<String, Object>();public static Object getBean(String className) {synchronized (ioc) {if (!ioc.containsKey(className)) {Object obj = null;try {obj = Class.forName(className).newInstance();ioc.put(className, obj);} catch (Exception e) {e.printStackTrace();}return obj;} else {return ioc.get(className);}}}
}

适用于需要大量创建单例对象的场景,便于管理。但是是非线程安全的。

5)线程单例


public class ThreadLocalSingleton {private static final ThreadLocal<ThreadLocalSingleton> threadLocalInstance =new ThreadLocal<ThreadLocalSingleton>() {@Overrideprotected ThreadLocalSingleton initialValue() {return new ThreadLocalSingleton();}};private ThreadLocalSingleton(){}public static ThreadLocalSingleton getInstance(){return threadLocalInstance.get();}
}
public class test {public static void main(String[] args) {System.out.println(ThreadLocalSingleton.getInstance());System.out.println(ThreadLocalSingleton.getInstance());System.out.println(ThreadLocalSingleton.getInstance());System.out.println(ThreadLocalSingleton.getInstance());Thread thread = new Thread(new ExectorThread());Thread thread1 = new Thread(new ExectorThread());thread.start();thread1.start();}
}

 主线程无论调用多少次,获取到的实例是同一个,都在连个子线程中分别获取到了不同的实例。

ThreadLocal将所有的对象全部放在ThreadLocalMap中,为每个线程都提供一个对象,实际上以空间换时间来实现线程隔离。

3、破坏单例

1)反射破坏单例

        try {Class<?> clazz = LazyInnerSingleton.class;Constructor constructor = clazz.getDeclaredConstructor(null);//强制访问constructor.setAccessible(true);//强制初始化Object o = constructor.newInstance();Object o1 = constructor.newInstance();System.out.println(o == o1);} catch (Exception e) {e.printStackTrace();}

 强制初始化后,出现两个不同的实例,优化重复创建抛出异常

public class LazyInnerClassSingleton {private LazyInnerClassSingleton() {//加个判断if(LazyHolder.LAZY != null){throw new RuntimeException("不允许创建多个实例");}}//默认不加载private static class LazyHolder {private static final LazyInnerClassSingleton LAZY = new LazyInnerClassSingleton();}//static是为了使单例的空间共享,保证方法不会被重写重载public static final LazyInnerClassSingleton getInstance() {//返回结果前,一定先加载内部类return LazyHolder.LAZY;}}

2)序列化破坏单例

单例创建好后,有时候需要将对象序列化再写入磁盘,读取的时候再进行反序列化,转化为内存对象。反序列化后的对象会重新分配内存。

public class SerializableSingleton implements Serializable {private SerializableSingleton() {}private final static SerializableSingleton INSTANCE = new SerializableSingleton();public static SerializableSingleton getInstance() {return INSTANCE;}}
        SerializableSingleton s1 = null;SerializableSingleton s2 = SerializableSingleton.getInstance();FileOutputStream fos = null;try {fos = new FileOutputStream("SerializableSingleton.obj");ObjectOutputStream oos = new ObjectOutputStream(fos);oos.writeObject(s2);oos.flush();oos.close();FileInputStream fis = new FileInputStream("SerializableSingleton.obj");ObjectInputStream ois = new ObjectInputStream(fis);s1 = (SerializableSingleton) ois.readObject();ois.close();System.out.println(s1);System.out.println(s2);System.out.println(s1 == s2);} catch (Exception e) {System.out.println(e);}

 反序列化后的对象和手动创建的对象是不同的

优化代码:

public class SerializableSingleton implements Serializable {private SerializableSingleton() {}private final static SerializableSingleton INSTANCE = new SerializableSingleton();public static SerializableSingleton getInstance() {return INSTANCE;}private Object readResolve(){return INSTANCE;}}

原理:

  •  isInstantiable(),判断构造方法是否为空,不为空返回true,只要有无参构造方法就会实例化。
  • hasReadResolveMethod(),就是通过反射找到一个无参的readResolve()方法并保存。
  • invokeReadResolve(),反射调用readResolveMethod()

虽然readResolve()方法返回实例解决了单例模式被破坏的问题,但实际上是实例化了两次,只不过新创建的对象没有被返回。如果创建对象的动作发生频繁加快,就以为这内存分配开销也会随之增大。

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

[保研/考研机试] KY3 约数的个数 清华大学复试上机题 C++实现

题目链接&#xff1a; KY3 约数的个数 https://www.nowcoder.com/share/jump/437195121691716950188 描述 输入n个整数,依次输出每个数的约数的个数 输入描述&#xff1a; 输入的第一行为N&#xff0c;即数组的个数(N<1000) 接下来的1行包括N个整数&#xff0c;其中每个…

Arcgis将一个shp依照属性表导出为多个shp

# -*- coding:utf-8 -*-import arcpy import osfrom arcpy import env#env.workspace "./" #自己设置路径shp rC:\Users\Administrator\Desktop\Lake\xxx.shp #shp文件路径outpath r"C:\Users\Administrator\Desktop\Lake\fenli" #输出结果路径with arc…

根据源码,模拟实现 RabbitMQ - 从需求分析到实现核心类(1)

目录 一、需求分析 1.1、对 Message Queue 的认识 1.2、消息队列核心概念 1.3、Broker Server 内部关键概念 1.4、Broker Server 核心 API &#xff08;重点实现&#xff09; 1.5、交换机类型 Direct 直接交换机 Fanout 扇出交换机 Topic 主题交换机 1.6、持久化 1.7…

【脚踢数据结构】队列(顺序和链式)

(꒪ꇴ꒪ )&#xff0c;Hello我是祐言QAQ我的博客主页&#xff1a;C/C语言,Linux基础,ARM开发板&#xff0c;软件配置等领域博主&#x1f30d;快上&#x1f698;&#xff0c;一起学习&#xff0c;让我们成为一个强大的攻城狮&#xff01;送给自己和读者的一句鸡汤&#x1f914;&…

通过OpenTelemetry上报Python-flask应用数据(阿里云)

参考文档 https://help.aliyun.com/document_detail/611711.html?spma2c4g.90499.0.0.34a056ddTu2WWq 先按照 方法一&#xff1a;手动埋点上报Python应用数据 步骤测试上报是否正常。 flas 上报 在 手动埋点上报Python应用数据 的基础上&#xff0c;上报flask应用的数据&#…

云计算-知识点大纲

前言&#xff1a;云计算的基本概念学习&#xff0c;基础知识大纲梳理。 目录 云计算的概念 云计算的特征 部署模式 服务模式 云计算的发展 云计算的核心技术 虚拟化技术 常见的虚拟化技术 服务器虚拟化 裸金属型技术 服务器虚拟化技术的特点 存储虚拟化 CPU 内存…

31 | 独角兽企业数据分析

独角兽企业:是投资行业尤其是风险投资业的术语,一般指成立时间不超过10年、估值超过10亿美元的未上市创业公司。 项目目的: 1.通过对独角兽企业进行全面地分析(地域,投资方,年份,行业等),便于做商业上的战略决策 项目数据源介绍 1.数据源:本项目采用的数据源是近…

机器学习鱼书笔记(自用更新)

零、预知识 1.Numpy 使用 介绍&#xff1a;高效的操作多维数组的函数库。 安装&#xff1a;&#xff08;前提已经安装了python&#xff09; pip install numpy导入 import numpy as np创建数组 Numpy最重要的数据结构是多维数组&#xff08;ndarray&#xff09;。通过Numpy&…

【Go语言】Golang保姆级入门教程 Go初学者chapter2

【Go语言】变量 VSCode插件 setting的首选项 一个程序就是一个世界 变量是程序的基本组成单位 变量的使用步骤 [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zuxG8imp-1691479164956)(https://cdn.staticaly.com/gh/hudiework/imgmain/image-20…

解决MySQL与Redis缓存一致性的问题

背景 考试系统中&#xff0c;教师会在后台发布一场考试&#xff0c;考试会存储在MySQL和Redis里面&#xff0c;考试有时候是会出错的&#xff0c;我们需要后台修改&#xff0c;如果多个教师在后台并发修改&#xff08;概率不大&#xff09;&#xff0c;可能会出现数据库缓存不…

Linux shell yes命令(不停输出换行的y)(不停输出换行的指定字符串)(脚本自动确认y)

文章目录 yes命令功能doc文档英文中文翻译完整文档 示例应用案例自动为脚本多次确认y yes命令功能 yes命令可以不断地输出换行的指定字符串&#xff0c;不加参数时&#xff0c;不断输出换行的“y”&#xff0c;有时我们需要执行一些需要用户键入“y”确认的脚本&#xff0c;但…

Mysql中如果建立了索引,索引所占的空间随着数据量增长而变大,这样无论写入还是查询,性能都会有所下降,怎么处理?

索引所占空间的增长确实会对MySQL数据库的写入性能和查询性能造成影响&#xff0c;这主要是由于索引数据过多时会导致磁盘I/O操作变得非常频繁&#xff0c;从而使性能下降。为此&#xff0c;可以采取以下几种方式来减缓这种影响&#xff1a; 1. 限制索引的大小&#xff1a;可以…

Oracle-创建PDB

Oracle-创建PDB 创建PDB的方式 从PDB$SEED新建PDB克隆已存在的PDB 本地PDB克隆到同一个CDB中将远程PDB克隆到CDB中将非CDB插入或克隆到CDB中通过插拔的方式创建PDB sql 命令语法 条件 CDB必须open并且read write模式连接CDB$ROOT 用户并且具有CREATEPLUGGABLEDATABASE系统权…

Linux 使用gdb调试C程序

一、gdb的一些基础命令 l&#xff1a;显示代码 l n&#xff1a;跳转到当前代码页的第n行的代码 l filename.c &#xff1a;n&#xff1a;跳转到filename.c文件的第n行代码 b 行号&#xff1a;加断点 info break&#xff1a;查看断点信息 delete 断点编号&#xff1a;删除断点 …

IoTDB原理剖析

一、介绍 IoTDB&#xff08;物联网数据库&#xff09;是一体化收集、存储、管理与分析物联网时序数据的软件系统。 Apache IoTDB采用轻量式架构&#xff0c;具有高性能和丰富的功能。 IoTDB从存储上对时间序列进行排序&#xff0c;索引和chunk块存储&#xff0c;大大的提升时序…

Python爬虫如何更换ip防封

作为一名长期扎根在爬虫行业动态ip解决方案的技术员&#xff0c;我发现很多人常常在使用Python爬虫时遇到一个困扰&#xff0c;那就是如何更换IP地址。别担心&#xff0c;今天我就来教你如何在Python爬虫中更换IP&#xff0c;让你的爬虫不再受到IP封锁的困扰。废话不多说&#…

linux安装wkhtmltopdf(清晰明了)

概述 在公司项目中使用到 wkhtmltopdf 转换PDF&#xff0c;由于 wkhtmltox-0.12.5 版本 echarts 图形虚线样式&#xff0c;需要升级 wkhtmltox-0.12.6 版本来解决。 官网地址 wkhtmltopdf &#xff1a;https://wkhtmltopdf.org/ windows 安装 下载流程及安装流程 进入官…

gazebo 导入从blender导出的dae等文件

背景&#xff1a; gazebo 模型库里的模型在我需要完成的任务中不够用&#xff0c;还是得从 solidworks、3DMax, blender这种建模软件里面在手动画一些&#xff0c;或者去他们的库里面在挖一挖。 目录 1 blender 1-1 blender 相关links 1-2 install 2 gazebo导入模型 2-1 g…

开封Geotrust单域名https证书推荐

Geotrust作为全球领先的数字证书颁发机构之一&#xff0c;拥有多年的数字证书颁发经验&#xff0c;其数字证书被广泛应用于电子商务、在线支付、企业通讯、云计算等领域&#xff0c;为用户提供了安全可靠的保障。而Geotrust旗下的单域名https证书是大多数客户创建网站时的选择之…

技术应用:Docker安全性的最佳实验|聊聊工程化Docker

&#x1f525; 技术相关&#xff1a;《技术应用》 ⛺️ I Love you, like a fire! 文章目录 首先&#xff0c;使用Docker Hub控制访问其次&#xff0c;保护密钥写在最后 不可否认&#xff0c;能生存在互联网上的软件都是相互关联的&#xff0c;当我们开发一款应用程序时&#x…