当前位置: 首页 > news >正文

【Java多线程(4)】案例:设计模式

目录

一、什么是设计模式?

二、单例模式

1. 饿汉模式

2. 懒汉模式

懒汉模式-第一次改进

懒汉模式-第二次改进

懒汉模式-第三次改进


一、什么是设计模式?

设计模式是针对软件设计中常见问题的通用解决方案。它们提供了一种被广泛接受的方法来解决特定类型的问题,并且具有经过验证的效果和可重复使用性。设计模式不是代码或类库,而是一种解决问题的思维方式或模式。

设计模式就好比象棋中的“棋谱”,针对对方的一些走法,黑方应招的时候有一些固定的套路,按照套路走局势就不会吃亏。想要成为一名象棋高手,背棋谱其实是必然的。因此,设计模式也是开发中的一种重要的解决问题的方式。

二、单例模式

单例模式是校招中 最常考的设计模式 之⼀。
单例模式能保证某个类在程序中只存在唯一一份实例,而不会创建出多个实例。
单例模式具体的实现方式有很多,最常见的是" 饿汉"和" 懒汉"两种。

1. 饿汉模式

饿汉式单例(Eager Initialization):在类加载时就创建实例。

// 单例模式 - 饿汉模式
// 类加载的同时,直接创建实例。
class Singleton {// 在类加载时就创建实例private static Singleton instance = new Singleton();// 对外提供获取实例的静态方法public static Singleton getInstance() {return instance;}// 私有化构造方法,防止外部直接实例化private Singleton() {}
}public class Demo1 {public static void main(String[] args) {// 获取单例对象Singleton s1 = Singleton.getInstance();Singleton s2 = Singleton.getInstance();// 判断两个实例是否相同System.out.println(s1 == s2);  // 输出 true,说明两个引用指向同一个实例// 以下代码会报错,因为构造方法是私有的,无法在外部直接实例化// Singleton s = new Singleton();}
}

上述代码类加载就会创建实例的原因:

在Java中,类加载时机是在首次使用该类时,Java虚拟机会负责对类进行加载、连接和初始化。在加载阶段,虚拟机会加载类的字节码并创建Class对象,而在初始化阶段,虚拟机会执行类的初始化过程,其中包括对静态变量的初始化。因此,在首次使用该类时,类会被加载并且静态变量会被初始化,从而创建单例实例

通过对构造方法的私有化,使得上述代码只有一个实例。

由于单例对象在类加载时就被创建,因此不存在线程安全问题。但如果实例很大且长时间未使用,会造成资源浪费。

2. 懒汉模式

懒汉式单例(Lazy Initialization):在第一次调用时创建实例。

public class Singleton {private static Singleton instance = null;private Singleton() {// 私有化构造方法}public static Singleton getInstance() {if (instance == null) {instance = new Singleton();}return instance;}
}

注意:此示例不是线程安全的【Java多线程(3)】线程安全问题和解决方案

线程安全问题发生在首次创建实例时,如果多个线程中同时调用getInstance方法,由于线程的抢占式执行,就可能导致创建出多个实例。

如果实例创建好了,后面在多线程环境调用getInstance就不再有线程安全问题了,因为不会再new实例了。

因此,加上synchronized 就能够解决这里的创建多个实例的问题

懒汉模式-第一次改进

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

这样操作后,就能保证只有第一个调用getInstance方法的线程会创建实例,其余线程即使抢到CPU执行权,也会被阻塞。后续条件判断的时候也就不会再new了。

但是,饿汉模式只有在最开始调用getInstance会存在线程安全问题,后续再调用是没有线程安全问题的。而上述代码针对后续调用,明明没有线程安全问题,却还是要加锁(可能导致其他线程阻塞),这使得代码的性能大大降低了。

因此,对于这个问题,还要进行一些改进,就是只在对象还未实例化的时候对实例化对象的这段代码进行加锁

懒汉模式-第二次改进

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

使用双重if判定,降低锁竞争的频率。

  1. 在 getInstance() 方法中首先检查 instance 是否为 null,如果是 null,表示尚未创建实例,需要进行实例化操作。
  2. 由于该方法可能被多个线程同时调用,因此需要使用双重检查锁定来确保只有一个线程创建实例。
  3. 在第一次检查 instance 为 null 后,进入同步块,并再次检查 instance 是否为 null,以防止多个线程同时进入同步块后重复创建实例。
  4. 如果 instance 仍然为 null,则在同步块内部创建新的 SingletonLazy 实例,并将其赋值给 instance

这样的做法,即使在对象还未实例化的时候,有多个线程进入第一个if判断了,里面的锁仍会保证只有一个线程会去实例化,并且在后续线程再调用getInstance方法的时候,外层的if判断就把它挡住了,就不会再上锁了。

但是,写出双重if判定的代码的时候,强大的IDEA就已经给出了一个警告:双重检查锁定

既然IDEA都给警告了,意味着这里可能还会问题存在!

懒汉模式-第三次改进

IDEA给我们的处理方式是:给instance加上volatile关键字。

一方面,这里就又涉及到了内存可见性问题:在第一次创建实例中,被阻塞的线程有可能没有感知到instance的引用已经改变了,导致的内存可见性问题。

另一方面,就是我们在【Java多线程(3)】线程安全问题和解决方案 这篇博客中还未解决的指令重排序问题,这是我们这里要讨论的重点

指令重排序,也是编译器的一种优化策略。看一个去超市买菜的例子:

可以看到,优化后的策略节省了不少时间。

而在instance = new SingletonLazy(); 这行代码中,其实会有很多很多的指令,但是大体上可以分成三个步骤:

  1. 申请内存空间
  2. 调用构造方法(对内存空间进行初始化)
  3. 把此时内存空间的地址,赋值给 instance 引用

而在指令重排序的优化下,上述过程不一定是按 123 执行的,也可能是 132 执行(1一定先执行),这种优化策略,在单线程下都是没有问题的,但 132 在多线程下,可能就会引起bug。假设有t1和t2两个线程,线程间是按照以下顺序执行的:

volatile解决的就是上述两个问题(内存可见性和指令重排序(保证执行顺序是123))

因此,懒汉模式的最终代码就是在第二次改进的基础上,给instance加上volatile关键字。

//懒汉模式-最终代码
class SingletonLazy {private static volatile SingletonLazy instance = null;public static SingletonLazy getInstance() {if (instance == null) {synchronized (SingletonLazy.class) {if (instance == null) {instance = new SingletonLazy();}}}return instance;}private SingletonLazy() {}
}

相关文章:

【Java多线程(4)】案例:设计模式

目录 一、什么是设计模式? 二、单例模式 1. 饿汉模式 2. 懒汉模式 懒汉模式-第一次改进 懒汉模式-第二次改进 懒汉模式-第三次改进 一、什么是设计模式? 设计模式是针对软件设计中常见问题的通用解决方案。它们提供了一种被广泛接受的方法来解决…...

时序预测 | Matlab实现CPO-BiLSTM【24年新算法】冠豪猪优化双向长短期记忆神经网络时间序列预测

时序预测 | Matlab实现CPO-BiLSTM【24年新算法】冠豪猪优化双向长短期记忆神经网络时间序列预测 目录 时序预测 | Matlab实现CPO-BiLSTM【24年新算法】冠豪猪优化双向长短期记忆神经网络时间序列预测效果一览基本介绍程序设计参考资料 效果一览 基本介绍 1.Matlab实现CPO-BiLST…...

java面试题(4)|Spring和Spring Boot之间有什么关联和区别

文章目录 Spring和Spring Boot的有什么关联?Spring和Spring Boot有什么区别?如何快速区分某个项目采用的是 Spring 还是 Spring Boot? Spring和Spring Boot的有什么关联? Spring Boot是建立在Spring框架之上的,因此它…...

Spring Boot中前端通过请求接口下载后端存放的Excel模板

导出工具类 package com.yutu.garden.utils;import com.baomidou.mybatisplus.core.toolkit.ObjectUtils; import org.apache.commons.io.IOUtils; import org.apache.poi.hssf.util.HSSFColor; import org.apache.poi.xssf.usermodel.XSSFWorkbook; import org.slf4j.Logger;…...

构建企业级微服务平台:实现可扩展性、弹性和高效性

在软件开发的快速发展领域中,企业不断努力构建健壮、可扩展和高效的系统。随着微服务架构的出现,再加上云原生技术的应用,创建敏捷且具有弹性的平台的可能性是无限的。在本指南中,我们将深入探讨使用强大的工具和技术组合&#xf…...

存内计算技术在边缘计算、物联网设备中的应用及前景

存内计算技术简介 存内计算技术是一种新兴的计算范式,其核心理念是将存储和计算功能集成在同一硬件单元中。这种技术的优势在于能够在存储单元内部直接进行计算操作,从而减少数据在存储器和处理器之间的传输,提高计算效率。 以下是存内计算技…...

C#使用Selenium驱动Chrome浏览器

1.Selenium库依赖安装 Selenium WebDriver是Selenium项目的一部分,用于模拟用户在Web应用程序中的交互操作。它支持多种浏览器,如Chrome、Firefox、IE等,且与各种编程语言(如Java、Python、C#等)兼容,具有…...

【软件工程】详细设计(二)

这里是详细设计文档的第二部分。前一部分点这里 4. 学生端模块详细设计 学生端模块主要由几个组件构成:学生登录界面,成绩查询界面等界面。因为学生端的功能相对来说比较单一,因此这里只给出两个最重要的功能。 图4.1 学生端模块流程图 4.…...

数据质量决定大模型能力,景联文科技提供高质量大模型数据

随着大模型的深入发展,各类资源要素的配置状态已悄然变化。其中,数据的价值已被提升到一个新高度。 大模型往往拥有庞大的参数和复杂的网络结构,需要大量的数据来学习和优化。数据的质量和数量直接决定了模型的训练效果。若数据不足或质量不佳…...

大话设计模式之状态模式

状态模式是一种行为设计模式,它允许对象在其内部状态发生变化时改变其行为。在状态模式中,对象将其行为委托给当前状态对象,从而在不同的状态下执行不同的行为,而不必在对象自身的代码中包含大量的条件语句。 通常,状…...

【机器学习】“强化机器学习模型:Bagging与Boosting详解“

1. 引言 在当今数据驱动的世界里,机器学习技术已成为解决复杂问题和提升决策制定效率的关键工具。随着数据的增长和计算能力的提升,传统的单一模型方法已逐渐无法满足高精度和泛化能力的双重要求。集成学习,作为一种结合多个学习算法以获得比…...

《QT实用小工具·九》设备按钮控件

1、概述 源码放在文章末尾 该项目实现了设备按钮控件,主要包含如下功能: 可设置按钮样式 圆形、警察、气泡、气泡2、消息、消息2。可设置按钮颜色 布防、撤防、报警、旁路、故障。可设置报警切换及对应报警切换的颜色。可设置显示的防区号。可设置是否…...

计算机服务器中了helper勒索病毒怎么办,helper勒索病毒解密流程步骤

随着网络技术在企业中的不断应用,越来越多的企业离不开网络,网络为企业提供了极大便利,大大提升了生产运营效率,由此而产生的网络数据安全问题也成为了企业关心的主要话题。近期,云天数据恢复中心接到多家企业的求助&a…...

qT 地图显示飞机轨迹

为了在Qt中显示飞机轨迹,你可以使用Qt的地图模块,例如Qt Location。以下是一个简单的示例,展示如何使用Qt地图模块显示飞机的轨迹。 首先,确保你的Qt项目文件(.pro)中包含了以下行来启用Qt Location模块: !include($…...

智慧展览馆:基于AI智能识别技术的视频智慧监管解决方案

一、建设背景 随着科技的不断进步和社会安全需求的日益增长,展览馆作为展示文化、艺术和科技成果的重要场所,其安全监控系统的智能化升级已成为当务之急。为此,旭帆科技(TSINGSEE青犀)基于视频智能分析技术推出了展览馆…...

Linux实用性脚本 [bash]

Linux实用性脚本 [bash] 1. 列出排名前 5 的CPU/内存占用进程2. iptables 自动屏蔽访问网站频繁的IP3. 自动发布 Java 项目(Tomcat)4. Nginx 访问日志分析脚本5. 查看网卡实时流量脚本6. 批量检测网站是否异常并邮件通知7. 目录入侵检测与告警8. 一键查看…...

Key exchange failed.No compatible key exchange method.

使用远程工具secureCRT (版本:Version 7.0.0 (x64 build 326) ) 连接Linux(Ubuntu-22.04)系统 若20之前的ubuntu系统版本参考:: 🔗使用远程工具连接Linux系统 1、检查ssh-server版本 sudo dpk…...

【FAQ】HarmonyOS SDK 闭源开放能力 —Asset Store Kit

1.问题描述 使用关键资产API需要配置SystemCapability.Security.Asset,但不知道syscap.json文件应该配置在哪里,文档也没找到。 解决方案 新增关键资产等API可以直接参考开发指南里的示例代码进行调用: https://developer.huawei.com/cons…...

arm架构离线部署docker

1.首先检查服务器glibc版本 rpm -q glibc 如果 glibc-2.35版本以上,则参考下面安装步骤,包下载地址 链接:https://pan.baidu.com/s/1X64XrzRva0O7ZSekLZdIcA?pwd8k4c 提取码:8k4c glibc-2.35以上Docker离线安装 如下 mkdir …...

SpringBoot(48)-使用 SkyWalking 进行分布式链路追踪

Spring Boot(48)- 使用 SkyWalking 进行分布式链路追踪 介绍 在分布式系统中,了解各个服务之间的调用关系和性能表现是非常重要的。SkyWalking 是一款开源的分布式系统监控与分析平台,能够帮助我们实现分布式系统的链路追踪、性…...

铭豹扩展坞 USB转网口 突然无法识别解决方法

当 USB 转网口扩展坞在一台笔记本上无法识别,但在其他电脑上正常工作时,问题通常出在笔记本自身或其与扩展坞的兼容性上。以下是系统化的定位思路和排查步骤,帮助你快速找到故障原因: 背景: 一个M-pard(铭豹)扩展坞的网卡突然无法识别了,扩展出来的三个USB接口正常。…...

基于ASP.NET+ SQL Server实现(Web)医院信息管理系统

医院信息管理系统 1. 课程设计内容 在 visual studio 2017 平台上,开发一个“医院信息管理系统”Web 程序。 2. 课程设计目的 综合运用 c#.net 知识,在 vs 2017 平台上,进行 ASP.NET 应用程序和简易网站的开发;初步熟悉开发一…...

让AI看见世界:MCP协议与服务器的工作原理

让AI看见世界:MCP协议与服务器的工作原理 MCP(Model Context Protocol)是一种创新的通信协议,旨在让大型语言模型能够安全、高效地与外部资源进行交互。在AI技术快速发展的今天,MCP正成为连接AI与现实世界的重要桥梁。…...

Swagger和OpenApi的前世今生

Swagger与OpenAPI的关系演进是API标准化进程中的重要篇章,二者共同塑造了现代RESTful API的开发范式。 本期就扒一扒其技术演进的关键节点与核心逻辑: 🔄 一、起源与初创期:Swagger的诞生(2010-2014) 核心…...

html-<abbr> 缩写或首字母缩略词

定义与作用 <abbr> 标签用于表示缩写或首字母缩略词&#xff0c;它可以帮助用户更好地理解缩写的含义&#xff0c;尤其是对于那些不熟悉该缩写的用户。 title 属性的内容提供了缩写的详细说明。当用户将鼠标悬停在缩写上时&#xff0c;会显示一个提示框。 示例&#x…...

Spring Cloud Gateway 中自定义验证码接口返回 404 的排查与解决

Spring Cloud Gateway 中自定义验证码接口返回 404 的排查与解决 问题背景 在一个基于 Spring Cloud Gateway WebFlux 构建的微服务项目中&#xff0c;新增了一个本地验证码接口 /code&#xff0c;使用函数式路由&#xff08;RouterFunction&#xff09;和 Hutool 的 Circle…...

JVM 内存结构 详解

内存结构 运行时数据区&#xff1a; Java虚拟机在运行Java程序过程中管理的内存区域。 程序计数器&#xff1a; ​ 线程私有&#xff0c;程序控制流的指示器&#xff0c;分支、循环、跳转、异常处理、线程恢复等基础功能都依赖这个计数器完成。 ​ 每个线程都有一个程序计数…...

排序算法总结(C++)

目录 一、稳定性二、排序算法选择、冒泡、插入排序归并排序随机快速排序堆排序基数排序计数排序 三、总结 一、稳定性 排序算法的稳定性是指&#xff1a;同样大小的样本 **&#xff08;同样大小的数据&#xff09;**在排序之后不会改变原始的相对次序。 稳定性对基础类型对象…...

C#学习第29天:表达式树(Expression Trees)

目录 什么是表达式树&#xff1f; 核心概念 1.表达式树的构建 2. 表达式树与Lambda表达式 3.解析和访问表达式树 4.动态条件查询 表达式树的优势 1.动态构建查询 2.LINQ 提供程序支持&#xff1a; 3.性能优化 4.元数据处理 5.代码转换和重写 适用场景 代码复杂性…...

关于uniapp展示PDF的解决方案

在 UniApp 的 H5 环境中使用 pdf-vue3 组件可以实现完整的 PDF 预览功能。以下是详细实现步骤和注意事项&#xff1a; 一、安装依赖 安装 pdf-vue3 和 PDF.js 核心库&#xff1a; npm install pdf-vue3 pdfjs-dist二、基本使用示例 <template><view class"con…...