【Java】多线程案例(单例模式,阻塞队列)

> :heart: Author:    老九

☕️ 个人博客:老九的CSDN博客
🙏 个人名言:不可控之事 乐观面对
😍 系列专栏:

文章目录

  • 实现安全版本的单例模式
    • 饿汉模式
      • 类和对象的概念
      • 类对象
      • 类的静态成员与实例成员
    • 懒汉模式
      • 如何保证懒汉模式的线程安全
  • 阻塞队列
    • 让多个服务器之间充分解耦
    • 能让请求进行 "削峰填谷"
      • 标准库中的阻塞队列
      • 自己实现阻塞队列

实现安全版本的单例模式

  • 单例模式是设计模式之一。代码当中的某个类,只能有一个实例,不能有多个。单例模式分为:饿汉模式和懒汉模式

饿汉模式

饿汉模式表示很着急,就想吃完饭剩下很多碗,然后一次性把碗全洗了。就是比较着急的去创建实例。用static来创建实例,利用在类加载时初始化,只有一份拷贝存在于内存中的特性实现单例模式,并且立即进行实例化。下面代码中的instance对应的实例,就是该类唯一的实例:

class Singleton{public static Singleton instance = new Singleton();private Singleton(){}public static Singleton getInstance(){return instance;}
}public class Example{public static void main(String[] args) {Singleton instance = Singleton.getInstance();Singleton instance2 = Singleton.getInstance();System.out.println(instance == instance2);}
}

为了防止程序员在其他地方不小心new这个Singleton,于是把构造方法设为private了

类和对象的概念

类是对象的模板,描述了对象的行为和状态。
对象是类的实例,它是在内存中分配的实体,具有实际的属性和行为。

类对象

在Java中,每个类在加载到内存后,都会有一个对应的类对象。这个类对象存储了类的相关信息,包括类的名称、方法、属性等。
类对象是Java虚拟机(JVM)在运行时对类的抽象表示。

类的静态成员与实例成员

静态成员(类属性/类方法)是与类关联的,而不是与类的实例相关联的。它们在类加载时初始化,并且只有一份拷贝存在于内存中,被所有类的实例共享。
实例成员(实例属性/实例方法)是与类的实例相关联的,每个类的实例都有自己的一份实例成员。

懒汉模式

懒汉模式主要是,不立即初始化实例,只有在被调用的时候,才会创建实例

如何保证懒汉模式的线程安全

加锁,把创建实例的代码加锁就可以了,加锁的时候,可以直接指定类对象.class作为锁对象。加锁之后,线程安全问题得到了解决,但是又有了新的问题。在多线程调用获取信息的时候,可能涉及到读和修改,但是一旦实例被初始化之后,就只剩读操作了。

class Singleton{private static volatile Singleton instance = null;private Singleton(){}public static Singleton getInstance(){if(instance == null){synchronized (Singleton.class){if(instance == null){instance = new Singleton();}}}return instance;}
}public class Example{public static void main(String[] args) {Singleton instance = Singleton.getInstance();Singleton instance2 = Singleton.getInstance();System.out.println(instance == instance2);}
}

为什么要两次判断: 因为在并发环境中,多个线程可能会同时通过第一次检查,此时可能会出现多个线程都创建实例的情况。第二次检查可以确保只有一个线程能够创建实例,保证了单例模式的唯一性。
为什么需要加volatile: 因为如果有多个线程的话,都去读getInstance就可能导致内存可见性的问题,所以需要加上volatile来避免内存可见性问题
和饿汉模式的区别就是,懒汉模式只有在使用的时候,才会创建实例,饿汉模式在类加载的时候就会创建实例。

阻塞队列

队列的特性是先进先出,相对于普通队列,阻塞队列又有其他方面的功能:

  1. 线程安全
  2. 产生阻塞效果:
    a. 如果队列为空,尝试出队列,就会出现阻塞,阻塞到队列不为空为止。
    b. 如果队列为满,尝试入队列,就会出现阻塞,阻塞到队列不为满为止。

通过上面这种特性,就可以实现 “生产者消费者模型” 。就像我们烤串,有人烤,有人吃,然后烤好的放在烤盘上面。对于吃烤串来说,烤盘就是交易场所。此处的阻塞队列就可以作为生产者消费者模型当中的交易场所。

让多个服务器之间充分解耦

生产者消费者模型,是实际开发当中非常有用的一种多线程开发手段,尤其是在服务器开发场景当中。假设有两个服务器 A 和 B,A 作为入口服务器直接接受用户的网络请求,B 作为应用服务器,来给 A 提供一些数据。如图:
在这里插入图片描述
如果不使用生产者消费者模型,此时 A 和 B 的耦合性是比较强的。在开发 A 代码的时候,就得充分了解到 B 提供的一些接口,开发 B 代码的时候,也得充分了解到 A 是怎么调用的。一旦想把 B 换成 C ,A 的代码就需要较大的改动。而且如果 B 挂了,也可能直接导致 A 也顺带挂了。
使用生产者消费者模型,就可以降低这里的耦合,就像这样:
在这里插入图片描述

能让请求进行 “削峰填谷”

未使用生产者消费者模型的时候,如果请求量突然暴涨。A 暴涨=> B 暴涨,A 作为入口服务器,计算量较小,不会产生问题。B 作为应用服务器,计算量可能很大,需要的系统资源也更多,如果请求更大了,就可能导致程序挂了。如图:
在这里插入图片描述
如果使用阻塞队列的话,A 的请求暴涨 => 阻塞队列的请求暴涨,由于阻塞队列没啥计算量,只是存数据,所以抗压能力就更强。B 这边依然按照原来的速度进行处理数据,就不会受到 A 的暴涨。所以就不会引起崩溃。也就是 “削峰”。这种峰值很多时候不是持续的,过去之后就恢复了。B 仍然是按照原有的频率来处理之前积压的数据,就是 “填谷” 。
实际开发当中:阻塞队列不是一个简单的数据结构了,而是一个/一组专门的服务器程序,提供的功能不仅仅是队列阻塞。还会在这些基础上面提供更多的功能(数据持久化存储,多个数据通道,多节点备份,支持控制面板,方便配置参数),又叫”消息队列“。

标准库中的阻塞队列

通过 BlockingQueue 来实现阻塞队列,代码如下:

public class Example{public static void main(String[] args) throws InterruptedException {BlockingDeque<String> stringBlockingDeque = new LinkedBlockingDeque<>();//入队stringBlockingDeque.put("hello");//出队String s = stringBlockingDeque.take();System.out.println(s);}
}

自己实现阻塞队列

1.先实现一个普通队列(通过数组来实现)
2.再加上线程安全
3.再加上阻塞
实现一个普通队列:
在这里插入图片描述
出队列就是把 head 位置的元素返回去,并且 head++。当 tail 加满的时候,就回到队列头。所以重要的就是区别空队列和满队列。所以我们创建一个变量来记录元素的个数:size == 0 就是空,size == arr.length 就是满。
保证线程安全:
1.在多线程环境下,使用入队和出队没有问题。
2.入队和出队的代码是属于公共操作变量,所有给整个方法加锁。
实现阻塞效果:
通过使用 wait 和 notify 机制来实现阻塞效果。
对于 入队 来说:就是队列为满。
对于 出队 来说:就是队列为空。
代码如下 :

class MyBlockQueue{private int[] data = new int[1000];private int size = 0;private int head = 0;private int tail = 0;private Object locker = new Object();//入队列public void put(int value) throws InterruptedException {synchronized (locker){if(size == data.length){//put 当中的 wait 要由 take 来唤醒,只要 take 成功一个元素,就可以唤醒了locker.wait();}//队列不满,把新的元素放入 tail 位置上data[tail] = value;tail++;//处理 tail 到达数组末尾的情况if(tail >= data.length){tail = 0;}size++;locker.notify();}}//出队列public Integer take() throws InterruptedException {synchronized (locker){if(size == 0){//说明队列为空,就需要等待,就需要 put 来唤醒locker.wait();}int ret = data[head];head++;if(head >= data.length){head = 0;}size--;//就说明 take 成功了。然后唤醒 put 中的等待。locker.notify();return ret;}}
}public class Example{private static MyBlockQueue queue = new MyBlockQueue();public static void main(String[] args) {//如果有多个生产者和多个消费者,就再多创建几个线程Thread producer = new Thread(()->{int num = 0 ;while(true){try{System.out.println("生产了:"+num);queue.put(num);num++;}catch (InterruptedException e){e.printStackTrace();}}});producer.start();Thread customer = new Thread(()->{while(true){int num = 0;try{num = queue.take();System.out.println("消费了:"+num);//消费慢,但是可以一直生产。1000 之后,队列满了,所以就阻塞了。直到消费了一个。Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}});customer.start();}}

在这里插入图片描述
put和take的相互唤醒之间的关系如下:
在这里插入图片描述


♥♥♥码字不易,大家的支持就是我坚持下去的动力♥♥♥
版权声明:本文为CSDN博主「亚太地区百大最帅面孔第101名」的原创文章

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

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

相关文章

vulnhub靶机Venus

下载地址&#xff1a;The Planets: Venus ~ VulnHub 主机发现 arp-scan -l 端口扫描 nmap --min-rate 1000 -p- 192.168.21.132 端口版本扫描 nmap -sV -sT -O -p22,8080 192.168.21.132 对于http-alt HTTP Alternative Services 介绍 | JerryQu 的小站 (imququ.com) 总结…

课题学习(九)----阅读《导向钻井工具姿态动态测量的自适应滤波方法》论文笔记

一、 引言 引言直接从原论文复制&#xff0c;大概看一下论文的关键点&#xff1a; 垂直导向钻井工具在近钻头振动和工具旋转的钻井工作状态下&#xff0c;工具姿态参数的动态测量精度不高。为此&#xff0c;通过理论分析和数值仿真&#xff0c;提出了转速补偿的算法以消除工具旋…

亿图导出word和PDF中清晰度保留方法

步骤一 在亿图软件中画一个元件大小搭配合理的图。注意字体大小的安排&#xff0c;尤其是角标的大小要合适&#xff0c;示范如下 选中所有元器件&#xff0c;右键使用组合功能将电路图组合为一个整体 步骤二&#xff1a; 将亿图软件中的图保存为SVG格式。示范如下 在导出到…

数据防泄密软件排行榜

数据防泄密软件排行榜 安企神数据防泄密系统下载使用 现如今&#xff0c;随着信息技术的快速发展&#xff0c;数据泄密事件屡见不鲜。企业的隐私数据面临着越来越大的风险。为了保护数据的安全&#xff0c;数据防泄密软件应运而生。这些软件通过加密、监控和防护等功能&#…

数据结构例题代码及其讲解-图

01 图的邻接矩阵存储结构定义。 顶点表、边&#xff08;二维数组&#xff09;、顶点数量和边的数量 typedef struct MGraph {char Vex[MaxSize];//顶点(vertex)中数据int Edge[MaxSize][MaxSize];//边int vexnum, arcnum;//顶点数量和边的数量 }MGraph;图中涉及到.和->的区…

golang中的Interface接口 类型断言、接口赋值、空接口的使用、接口嵌套

Interface整理 文章目录 Interface整理接口嵌套接口类型断言类型判断 type-switch使用方法集与接口空接口实例 接口赋值给接口 接口是一种契约&#xff0c;实现类型必须满足它&#xff0c;它描述了类型的行为&#xff0c;规定类型可以做什么。接口彻底将类型能做什么&#xff0…

openGauss学习笔记-109 openGauss 数据库管理-管理用户及权限-角色

文章目录 openGauss学习笔记-109 openGauss 数据库管理-管理用户及权限-角色109.1 创建、修改和删除角色109.2 内置角色 openGauss学习笔记-109 openGauss 数据库管理-管理用户及权限-角色 角色是一组用户的集合。通过GRANT把角色授予用户后&#xff0c;用户即具有了角色的所有…

4.1 网络基础之网络IO

一、编写基本服务程序流程 下面介绍一个最最简单的服务程序的编写流程&#xff0c;先按照顺序介绍各个函数的参数和使用。然后在第三节用一对简单的程序对客户端与服务端通信过程进行演示。下面所有代码均在linux平台实现&#xff0c;所以可能与windows上的编程有所区别&#…

红队专题-从零开始VC++C/S远程控制软件RAT-MFC-远程桌面屏幕监控

红队专题 招募六边形战士队员[24]屏幕监控-(1)屏幕查看与控制技术的讲解图像压缩算法图像数据转换其他 [25]---屏幕监控(2)查看屏幕的实现7.1 屏幕抓图显示7.7 完善主控端 招募六边形战士队员 一起学习 代码审计、安全开发、web攻防、逆向等。。。 私信联系 [24]屏幕监控-(1…

vue源码分析(五)——vue render 函数的使用

文章目录 前言一、render函数1、render函数是什么&#xff1f; 二、render 源码分析1.执行initRender方法2.vm._c 和 vm.$createElement 调用 createElement 方法详解&#xff08;1&#xff09;区别&#xff08;2&#xff09;代码 3、原型上的_render方法&#xff08;1&#xf…

37基于MATLAB平台的图像去噪,锐化,边缘检测,程序已调试通过,可直接运行。

基于MATLAB平台的图像去噪&#xff0c;锐化&#xff0c;边缘检测&#xff0c;程序已调试通过&#xff0c;可直接运行。 37matlab边缘检测图像处理 (xiaohongshu.com)

大语言模型在天猫AI导购助理项目的实践!

本文主要介绍了Prompt设计、大语言模型SFT和LLM在手机天猫AI导购助理项目应用。 ChatGPT基本原理 “会说话的AI”&#xff0c;“智能体” 简单概括成以下几个步骤&#xff1a; 预处理文本&#xff1a;ChatGPT的输入文本需要进行预处理。 输入编码&#xff1a;ChatGPT将经过预…

Java毕业设计 SpringBoot 新能源充电桩管理系统

Java毕业设计 SpringBoot 新能源充电桩管理系统 SpringBoot 新能源充电桩管理系统 功能介绍 管理员 登录 验证码 注册 系统用户管理 普通用户管理 通知公告管理 留言管理 充电站管理 充电桩管理 充电桩预约 充电管理 订单管理 修改密码 普通用户 登录 修改个人资料 通知公告…

虹科分享|确保冻干工艺开发中精确测量和数据完整性的5步指南

文章来源&#xff1a;虹科环境监测技术 阅读原文&#xff1a;虹科分享 | 确保冻干工艺开发中精确测量和数据完整性的5步指南 一、介绍 冻干周期的工艺开发在冻干中起着至关重要的作用&#xff0c;因为它可以优化关键工艺参数&#xff0c;以实现理想的产品质量和工艺一致性。优…

MappingMongoConverter原生mongo 枚举类ENUM映射使用的是name

j.l.IllegalArgumentException: No enum constant com.xxx.valobj.TypeEnum.stringat java.lang.Enum.valueOf

*Django中的Ajax 纯js的书写样式1

搭建项目 建立一个Djano项目&#xff0c;建立一个app&#xff0c;建立路径&#xff0c;视图函数大多为render, Ajax的创建 urls.py path(index/,views.index), path(index2/,views.index2), views.py def index(request):return render(request,01.html) def index2(requ…

Python 读取 Word 详解(python-docx)

文章目录 1 概述1.1 第三方库&#xff1a;python-docx 2 新建文档2.1 空白文档2.2 标题2.3 段落2.4 文本2.5 字体2.6 图片2.7 表格 3 扩展3.1 修改文档3.2 读取文档 1 概述 1.1 第三方库&#xff1a;python-docx > pip install python-docx2 新建文档 2.1 空白文档 impo…

Python运维学习Day02-subprocess/threading/psutil

文章目录 1. 检测网段在线主机2. 获取系统变量的模块 psutil 1. 检测网段在线主机 import subprocessdef checkIP(ip):cmd fping -n 1 -w 1 {ip}null open(nlll,modewb)status subprocess.call(cmd,shellTrue,stdoutnull,stderrnull)if status 0:print(f"主机[{ip}]在…

ue5 右击.uproject generator vs project file 错误

出现如下错误 Unable to find valid 14.31.31103 C toolchain for VisualStudio2022 x64 就算你升级了你的 vs installer 也不好使 那是因为 在C:\Users\{YourUserName}\AppData\Roaming\Unreal Engine\UnrealBuildTool\BuildConfiguration.xml 这个缓存配置文件中写死了 14…

leetcode-链表

链表是一个用指针串联起来的线性结构&#xff0c;每个结点由数据域和指针域构成&#xff0c;指针域存放的是指向下一个节点的指针&#xff0c;最后一个节点指向NULL&#xff0c;第一个结点称为头节点head。 常见的链表有单链表、双向链表、循环链表。双向链表就是多了一个pre指…