【多线程】单例模式和阻塞队列

目录

一.单例模式

1. 饿汉模式

2. 懒汉模式

 二.阻塞队列

1. 阻塞队列的概念

2. BlockingQueue接口

3.生产者-消费者模型

4.模拟生产者-消费者模型


一.单例模式

单例模式(Singleton Pattern)是一种常用的软件设计模式,其核心思想是确保一个类只有一个实例,并提供一个全局访问点来获取这个实例。

为什么要引入单例模式?

单例模式的核心就是一个类中只有一个实例,因为只用管理一个实例,那么就可以更好的对代码进行一个校验和检查,方便高效管理,同时也避免了多个实例可能带来的问题。

如果创建一个实例需要耗费100G的资源,那么创建出多个实例,代价太大。而且在多数情况下,一个实例完全够用,所有没有必要创建出多个实例,这样就避免资源的重复创建和浪费


在编译器中,没有提供类只能创建出多少个实例的方法,但是我们可以通过一些代码逻辑去规定创建实例的要求,下面是常用的几种单例模式。

1. 饿汉模式

核心特点是 在类加载时就立即创建单例实例,并通过静态方法提供全局访问。

class Singleton{private static Singleton singleton = new Singleton();public static Singleton getSingleton(){return singleton;}//核心操作private Singleton(){}}
  1. 使用static关键字保证唯一实例(在类被加载的时候就会创建出这个唯一实例)
  2. 构造方法被设为私有,导致构造方法无法被调用
  3. 如果想要获取这个实例只能使用静态方法getSingleton调用

 由于在类被加载的时候,就会创建出实例,创建实例的时机很早(感觉非常的迫切,像一个饿汉),所有叫做饿汉模式

 注意:在多线程中,并发调用getSingleton静态方法,由于只有读操作,所以是线程安全

缺点: 如果实例未被使用,实例依然会被创建,可能造成资源浪费(假设实例的大小是100G)。


2. 懒汉模式

其核心特点是 延迟实例的创建,只有在第一次使用时才初始化单例对象,以减少资源浪费。

public class LazySingleton {private static LazySingleton instance;private LazySingleton() {}// 非线程安全public static LazySingleton getInstance() {if (instance == null) {//这里会涉及指令重排序的问题instance = new LazySingleton();}return instance;}
}

在使用调用方法时,只有在第一次使用时才初始化实例,否则都是返回已经存在的实例


注意: 在多线程中,并发调用getInstance方法,由于同时存在两个线程修改一个变量操作,线程不安全

 发现出现new两次情况,所以解决这个问题,我们要进行加锁

class LazySingleton {private static LazySingleton instance;private LazySingleton() {}static Object A = new Object();// 非线程安全public static LazySingleton getInstance() {synchronized (A){if (instance == null) {//这里会涉及指令重排序的问题instance = new LazySingleton();}}return instance;}
}

 这里我们会发现,每次调用getInstance方法,都要进行加锁和解锁的步骤,这样的步骤开销很大

所以我们需要进行改进

    public static LazySingleton getInstance() {if(instance==null){synchronized (A){if (instance == null) {//这里会涉及指令重排序的问题instance = new LazySingleton();}}}return instance;}
  • 第一次的  if  语句判断是否需要加锁
  • 第二次的  if   语句判断是否为空

 写到这里我们的代码还存在一个很严重的问题,由于指令重排序引起的线程安全问题

                    instance = new LazySingleton();

 在创建一个实例的时候,主要有3步骤:正确的步骤顺序是1—>2—>3

1. 在内存中开辟一份空间2.使用构造方法去创建实例,3. 将空间的地址赋值给引用变量

但是JVM可能会将步骤的执行顺序发送改变1—>3—>2,从而引发线程安全问题


具体原因:t2线程没有进入因为锁阻塞这步,t2线程会在t1线程执行完地址赋值后,刚好执行第一次的 if 判断语句,发现引用变量不为空,会直接返回引用变量(但是引用变量的值是空的) 

其实解决也很简单使用volatile关键字

class LazySingleton {private static  volatile LazySingleton instance;private LazySingleton() {}static Object A = new Object();// 非线程安全public static LazySingleton getInstance() {if(instance==null){synchronized (A){if (instance == null) {//这里会涉及指令重排序的问题instance = new LazySingleton();}}}return instance;}
}

 二.阻塞队列

1. 阻塞队列的概念

阻塞队列可以看成是普通队列的一种扩展,遵循先进先出的原则,核心特性是当队列操作无法立即执行时,线程会被自动阻塞直到条件满足。

  • 如果队列是满的,进行入队列操作则会被阻塞,直到队列不满
  • 如果队列是空的,进行出队列操作则会被阻塞,直到队列不空

2. BlockingQueue接口

在 Java 标准库中内置了阻塞队列. 如果我们需要在一些程序中使用阻塞队列, 直接使用标准库中的

  • BlockingQueue接口属于java.util.concurrent包。它是线程安全的队列,支持阻塞操作。
  • 常见的实现类有:ArrayBlockingQueue,LinkedBlockingQueue,PriorityBlockingQueue
方法说明

put(E e)

队列未满时插入元素;若队列已满,则阻塞线程直到有空位。

take()

队列非空时取出元素;若队列为空,则阻塞线程直到有元素可用。

offer(E e)

队列未满时插入元素并返回 true;队列已满时直接返回 false(非阻塞)。

poll()

队列非空时取出元素并返回;队列为空时返回 null(非阻塞)。

offer(E e, long timeout, TimeUnit unit)

队列满时等待指定超时时间,超时后返回 false

poll(long timeout, TimeUnit unit)

队列空时等待指定超时时间,超时后返回 null

E peek()返回队列头部元素但不移除;队列空时返回 null

其中只有put( )和take( )方法带有阻塞的效果

3.生产者-消费者模型

生产者-消费者模型用于解决多线程环境下的线程同步问题,核心思想是通过 共享缓冲区(阻塞队列) 解耦生产者和消费者,使两者可以独立并发工作

生产者和消费者彼此之间不直接进行联系,而是通过缓冲区(阻塞队列)进行联系 

好处

(1)解耦合

  1. 解耦生产者和消费者的直接依赖,生产者和消费者可独立开发,提高开发效率,方便维护
  2.  生产者和消费者可并行执行,最大化利用 CPU、I/O 等资源
  3. 即使生产者挂了,也不会影响消费者的正常工作(反之同理)

解耦合在分布式系统中很常见,比如服务器的整个功能并不是由一个服务器全部完成,而是由多个服务器完成,每个服务器完成一部分功能,最后通过服务器之间的网络通信,实现整个功能 

(2)缓冲机制

  1. 生产者突发大量请求时,队列暂存数据,避免消费者过载崩溃。
  2. 消费者处理慢时,队列累积任务,避免生产者因等待而阻塞。
  3. 可以将缓冲区作为“蓄水池”,协调速度差异
public class Demo_3 {public static void main(String[] args) {BlockingDeque<Integer> deque = new LinkedBlockingDeque<>(100);Thread t1 = new Thread(()->{while(true){try {Thread.sleep(300);Random random = new Random();int num = random.nextInt(101);System.out.println("生产数:"+ num);deque.put(num);} catch (InterruptedException e) {e.printStackTrace();}}},"生产者");t1.start();Thread t2 = new Thread(()->{while(true){try {Thread.sleep(500);int num = deque.take();System.out.println("消费数:"+num);} catch (InterruptedException e) {e.printStackTrace();}}},"消费者");t2.start();}
}

 

4.模拟生产者-消费者模型

核心:阻塞队列的实现

将其看成一个循环队列,其中两个核心的方法:put()和take()

put():入队列操作,如果队列为满则进入阻塞状态,由take()进行唤醒

take():出队列操作,如果队列为空则进入阻塞状态,由put()方法进行唤醒

在判断是否需要进入阻塞状态的时候,使用while语句,进行多次判断,如果使用if语句相当于一锤定音,在阻塞的状态下,可能会被notifyAll()唤醒,但是这时候队列中的空间并不足够(虚假唤醒),也有可能会出现连续唤醒的情况,最好的方式是再进行一次判断

class MyBlockingQueue{Object A = new Object();int[] elems = null;int right ;int tail ;int usedSize;MyBlockingQueue(int capacity){elems = new int[capacity];}//注意锁的位置//放入操作public void put(int elem) throws InterruptedException {synchronized (A){//如果满,不能放出while (usedSize>=elems.length){A.wait();}//没有满,正常放入elems[tail++] = elem;if(tail>=elems.length){tail = 0;}usedSize++;A.notify();}}public int take() throws InterruptedException {synchronized (A){//如果为空,不能取出while (usedSize == 0){A.wait();}//不为空,正常取出int elem = elems[right++];//另一种写法right = right%elems.length;usedSize--;A.notify();return elem;}}}

 点赞的宝子今晚自动触发「躺赢锦鲤」buff!

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

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

相关文章

终值定理的推导与理解

终值定理的推导与理解 终值定理是控制理论和信号处理中的一个重要工具&#xff0c;它通过频域的拉普拉斯变换来分析时间域函数的最终稳态值。具体来说&#xff0c;终值定理提供了一个简便的方法&#xff0c;利用 F ( s ) F(s) F(s)&#xff08; f ( t ) f(t) f(t) 的拉普拉斯…

每日c/c++题 备战蓝桥杯(二分答案模版)

在算法学习中&#xff0c;二分答案算法是一种非常高效且常用的技巧。它的核心思想是通过不断缩小搜索范围&#xff0c;逐步逼近目标答案。相比传统的暴力搜索&#xff0c;二分答案算法的时间复杂度通常为 O(logn)&#xff0c;特别适合处理大规模数据的查找问题。 本文将详细介…

NLP高频面试题(二十六)——RAG的retriever模块作用,原理和目前存在的挑战

在自然语言处理领域&#xff0c;检索增强生成&#xff08;Retrieval-Augmented Generation&#xff0c;简称RAG&#xff09;是一种将信息检索与文本生成相结合的技术&#xff0c;旨在提升模型的回答准确性和信息丰富度。其中&#xff0c;Retriever在RAG架构中扮演着关键角色&am…

第30周Java分布式入门 分布式基础

分布式基础课程笔记 一、什么是分布式&#xff1f; 1. 权威定义 分布式系统定义为&#xff1a;“利用物理架构形成多个自治的处理元素&#xff0c;不共享主内存&#xff0c;通过发送消息合作”。 2. 核心解释 物理架构与处理元素 &#x1f31f; 多台独立服务器/电脑&#x…

Vuex状态管理

Vuex Vuex是一个专为Vue.js应用程序开发的状态管理模式。它采用集中式管理应用的所有组件状态&#xff0c;并以相应的规则保证状态以一种可预测的方式发生变化。&#xff08;类似于在前端的数据库&#xff0c;这里的数据存储在内存当中&#xff09; 一、安装并配置 在项目的…

从代码学习深度学习 - 使用块的网络(VGG)PyTorch版

文章目录 前言一、VGG网络简介1.1 VGG的核心特点1.2 VGG的典型结构1.3 优点与局限性1.4 本文的实现目标二、搭建VGG网络2.1 数据准备2.2 定义VGG块2.3 构建VGG网络2.4 辅助工具2.4.1 计时器和累加器2.4.2 准确率计算2.4.3 可视化工具2.5 训练模型2.6 运行实验总结前言 深度学习…

Baklib激活企业知识管理新动能

Baklib核心技术架构解析 Baklib的底层架构以模块化设计为核心&#xff0c;融合知识中台的核心理念&#xff0c;通过分布式存储引擎与智能语义分析系统构建三层技术体系。数据层采用多源异构数据接入协议&#xff0c;支持文档、音视频、代码片段等非结构化数据的实时解析与分类…

小智机器人中的部分关键函数,FreeRTOS中`xEventGroupWaitBits`函数的详细解析

以下是对FreeRTOS中xEventGroupWaitBits函数的详细解析&#xff1a; 函数功能 xEventGroupWaitBits用于在事件组中等待指定的位被设置。它可以配置为等待任意一个位或所有位&#xff0c;并支持超时机制。 注意&#xff1a;该函数不能在中断中调用。 函数原型 EventBits_t xEv…

关注分离(Separation of Concerns)在前端开发中的实践演进:从 XMLHttpRequest 到 Fetch API

关注分离&#xff08;Separation of Concerns&#xff09;在前端开发中的实践演进&#xff1a;从 XMLHttpRequest 到 Fetch API 一、关注分离的核心价值 关注分离&#xff08;SoC&#xff09;是软件工程领域的重要设计原则&#xff0c;强调将系统分解为不同维度的功能模块&am…

C之(16)scan-build与clang-tidy使用

C之(16)scan-build与clang-tidy使用 Author: Once Day Date: 2025年3月29日 一位热衷于Linux学习和开发的菜鸟&#xff0c;试图谱写一场冒险之旅&#xff0c;也许终点只是一场白日梦… 漫漫长路&#xff0c;有人对你微笑过嘛… 全系列文章可参考专栏: Linux实践记录_Once_da…

在 Vue 项目中快速集成 Vant 组件库

目录 引言一、找到 src 下的App.js 写入代码。二、安装Vant三、解决 polyfill 问题四、查看依赖五、配置webpack六、引入 Vant七、在组件中使用 Vant八、在浏览器中查看样式总结 引言 在开发移动端 Vue 项目时&#xff0c;选择一个高效、轻量且功能丰富的组件库是提升开发效率…

“GPU 挤不动了?”——聊聊基于 GPU 的计算资源管理

“GPU 挤不动了?”——聊聊基于 GPU 的计算资源管理 作者:Echo_Wish “老板:为什么 GPU 服务器卡得跟 PPT 一样?” “运维:我们任务队列爆炸了,得优化资源管理!” 在 AI 训练、深度学习、科学计算的场景下,GPU 计算资源已经成为香饽饽。但 GPU 服务器贵得离谱,一台 A…

AI渗透测试:网络安全的“黑魔法”还是“白魔法”?

引言&#xff1a;AI渗透测试&#xff0c;安全圈的“新魔法师” 想象一下&#xff0c;你是个网络安全新手&#xff0c;手里攥着一堆工具&#xff0c;正准备硬着头皮上阵。这时&#xff0c;AI蹦出来&#xff0c;拍着胸脯说&#xff1a;“别慌&#xff0c;我3秒扫完漏洞&#xff0…

(二)GEE基础学习初探及案例详解【20250330】

Google Earth Engine(GEE)是由谷歌公司开发的众多应用之一。借助谷歌公司超强的服务器运算能力以及与NASA的合作关系&#xff0c;GEE平台将Landsat、MODIS、Sentinel等可以公开获取的遥感图像数据存储在谷歌的磁盘阵列中&#xff0c;使得GEE用户可以方便的提取、调用和分析海量…

redhat认证是永久的吗

​认证有效期 ​红帽认证一般有效期为3年​&#xff08;如RHCSA、RHCE、RHCA等&#xff09;&#xff0c;从通过考试之日起计算。 ​例外&#xff1a;部分基础或工程师认证&#xff08;如Red Hat Certified Engineer&#xff09;有效期为三年时间&#xff0c;以官方最新政策为准…

git --- cherry pick

git --- cherry pick cherry pick cherry pick Cherry Pick 是 Git 中的一个操作&#xff0c;它允许你选择某个分支的某次&#xff08;或多次&#xff09;提交&#xff0c;并将其应用到当前分支&#xff0c;而不会合并整个分支的所有更改。 cherry pick 的作用 只提取某个特定的…

妙用《甄嬛传》中的选妃来记忆概率论中的乘法公式

强烈推荐最近在看的不错的B站概率论课程 《概率统计》正课&#xff0c;零废话&#xff0c;超精讲&#xff01;【孔祥仁】 《概率统计》正课&#xff0c;零废话&#xff0c;超精讲&#xff01;【孔祥仁】_哔哩哔哩_bilibili 其中概率论中的乘法公式&#xff0c;老师用了《甄嬛传…

AI 的出现是否能替代 IT 从业者?

AI 的出现是否能替代 IT 从业者&#xff1f; AI 的快速发展正在深刻改变各行各业&#xff0c;IT 行业也不例外。然而&#xff0c;AI 并非完全替代 IT 从业者&#xff0c;而是与其形成互补关系。本文将从 AI 的优势、IT 从业者的不可替代性、未来趋势等方面&#xff0c;探讨 AI…

【leetcode100】有效的括号

1、题目描述 给定一个只包括 (&#xff0c;)&#xff0c;{&#xff0c;}&#xff0c;[&#xff0c;] 的字符串 s &#xff0c;判断字符串是否有效。 有效字符串需满足&#xff1a; 左括号必须用相同类型的右括号闭合。左括号必须以正确的顺序闭合。每个右括号都有一个对应的…

为什么使用Flask + uWSGI + Nginx 部署服务?

概述 在Python开发的web应用中&#xff0c;我们通常能够看到flask、uWSGI、Nginx出现在一起&#xff0c;他们之间的关系是什么&#xff1f;为什么总是被应用在一起&#xff1f; &#xfeff; 三者共同使用为了实现一个目的&#xff1a;客户端向服务端发送数据请求&#xff0c;服…