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

Java 三大并大特性-可见性介绍(结合代码、分析源码)

目录

​编辑

一、可见性概念

1.1 概念

二、可见性问题由来

2.1 由来分析

三、可见性代码例子

3.1 代码

3.2 执行结果

四、Java 中保证可见性的手段

4.1 volatile

4.1.1 优化代码

4.1.2 测试结果

4.1.3 volatile原理分析

4.1.3.1 查看字节码

4.1.3.2 hotspot 层面

4.1.3.3 volatile原理总结

4.2 synchronized

4.2.1 代码优化

4.2.2 测试结果

4.2.3 synchronized 原理分析

4.2.3.1 synchronized 修饰方法

4.2.3.1.1 源代码

4.2.3.1.2 执行结果

4.2.3.1.3 编译分析

4.2.3.2 synchronized 修饰代码块

4.2.3.2.1 源代码

4.2.3.2.2 执行结果

4.2.3.2.3 编译分析

4.3 Lock

4.3.1 优化代码

4.3.2 测试结果

4.3.3 Lock实现可见性原理分析

4.3.3.1 源码分析

4.3.3.2 总结

4.4 final

4.4.1 final 实现可见性原理分析


一、可见性概念

1.1 概念

可见性是指当一个线程修改了共享变量后,其他线程能够立即得知这个修改。

二、可见性问题由来

2.1 由来分析

可见性问题是在CPU位置出现的,CPU处理速度非常快,相对CPU来说,去主内存获取数据这个事情太慢了,为了解决CPU加载主内存数据慢的问题,就在CPU加入了缓存寄存器,分别为L1、L2、L3三级缓存,每次去主内存拿完数据后,就会存储到CPU的三级缓存,每次去三级缓存拿数据,效率肯定会提升。如下图所示:

引入这种缓存机制后,这就带来了问题,现在CPU都是多核,每个线程的工作内存(CPU三级缓存)都是独立的,会告知每个线程中做修改时,只改自己的工作内存,没有及时的同步到主内存,导致主内存和缓存之间数据不一致问题而数据不一致的问题,换据说法就是可见性问题。

为了解决CPU硬件层面的缓存一致性问题,于是就设计出了缓存一致性协议,其中比较典型的就是MESI协议,但是这个协议其实不同的CPU厂商的实现方式是有差异的,Java 层面为了屏蔽各种硬件和操作系统带来的差异,让并发编程做到真正意义上的跨平台,就设计出了JMM,即Java Memery Model, Java 内存模型来解决

三、可见性代码例子

3.1 代码

package com.ningzhaosheng.thread.concurrency.features.visible;/*** @author ningzhaosheng* @date 2024/2/5 19:36:39* @description 测试可见性*/
public class TestVisible {private static boolean flag = true;public static void main(String[] args) throws InterruptedException {Thread t1 = new Thread(() -> {while (flag) {// ....}System.out.println("t1线程结束");});t1.start();Thread.sleep(10);flag = false;System.out.println("主线程将flag改为false");}
}

3.2 执行结果

由结果可知,主线程修改了flag = false;但是并没有使t1线程里面的循环结束。

四、Java 中保证可见性的手段

4.1 volatile

4.1.1 优化代码

package com.ningzhaosheng.thread.concurrency.features.visible.volatiles;/*** @author ningzhaosheng* @date 2024/2/5 19:45:29* @description 测试volatile*/
public class TestVolatile {private volatile static boolean flag = true;public static void main(String[] args) throws InterruptedException {Thread t1 = new Thread(() -> {while (flag) {// ....}System.out.println("t1线程结束");});t1.start();Thread.sleep(10);flag = false;System.out.println("主线程将flag改为false");}
}

4.1.2 测试结果

由以上测试结果可以看到,使用volatile 修饰共享变量之后,在主线程修改了flag =false 之后,线程t1读取到了最新值,并结束了循环,结束了线程。那么为什么使用了volatile之后就能解决共享变量的问题呢?要回答这个问题其实综合性考虑的内容还比较多,涉及到CPU的多级缓存、计算机缓存一致性协议和Java 内存模型等相关内容。我们接下来就分析下吧。

4.1.3 volatile原理分析

4.1.3.1 查看字节码
javap -v .\TestVolatile.class

从以上截图我们可以看到,使用volatile修饰的变量,会多一个ACC_VOLATILE 指令关键字。我们接着去hotspot 查看c++源码,分析ACC_VOLATILE做了些什么操作。

4.1.3.2 hotspot 层面

根据ACC_VOLATILE指令关键字,我们可以在hotspot 源码中,找到他的内容:

jdk8u/jdk8u/hotspot: 69087d08d473 src/share/vm/utilities/accessFlags.hpp (openjdk.org)

接着,我们找下is_volatile:

jdk8u/jdk8u/hotspot: 69087d08d473 src/share/vm/interpreter/bytecodeInterpreter.cpp (openjdk.org)​​​​​​

 

从以上截图的这段代码中可以看到,会先判断tos_type(volatile变量类型),后面有不同的基础类型的调用,比如int类型就调用release_int_field_put,byte就调用release_byte_field_put等等。
判断完类型之后,我们可以看到代码后面执行的语句是:

我们可以在以下代码位置找到该源码:

jdk8u/jdk8u/hotspot: 69087d08d473 src/share/vm/runtime/orderAccess.hpp (openjdk.org)

实际上storeload() 这个方法,针对不同CPU有不同的实现,它的具体实现在src/os_cpu下,我们可以去看一下:

这里我们以linux_x86架构的CPU实现为例,我们去看下storeload()方法做了些什么操作。

jdk8u/jdk8u/hotspot: 69087d08d473 src/os_cpu/linux_x86/vm/orderAccess_linux_x86.inline.hpp (openjdk.org)

接着看下fence()函数:

通过这面代码可以看到lock;add1,其实这个就是内存屏障。lock;add1 $0,0(%%esp)作为cpu的一个内存屏障。
add1 $0,0(%%rsp)表示:将数值0加到rsp寄存器中,而该寄存器指向栈顶的内存单元。加上一个0,rsp寄存器的数值依然不变。即这是一条无用的汇编指令。在此利用add1指令来配合lock指令,用作cpu的内存屏障。

内存屏障:

这四个分别对应了经常在书中看到的JSR规范中的读写屏障LoadLoad屏障:(指令Load1; LoadLoad; Load2),在Load2及后续读取操作要读取的数据被访问前,保证Load1要读取的数据被读取完毕。

  • LoadStore屏障:(指令Load1; LoadStore; Store2),在Store2及后续写入操作被刷出前,保证Load1要读取的数据被读取完毕。
  • StoreStore屏障:(指令Store1; StoreStore; Store2),在Store2及后续写入操作执行前,保证Store1的写入操作对其它处理器可见。
  • StoreLoad屏障:(指令Store1; StoreLoad; Load2),在Load2及后续所有读取操作执行前,保证Store1的写入对所有处理器可见。它的开销是四种屏障中最大的。在大多数处理器的实现中,这个屏障是个万能屏障,兼具其它三种内存屏障的功能

对于volatile操作而言,其操作步骤如下:

  • 每个volatile写入之前,插入一个 StoreStore ,写入以后插入一个StoreLoad,JMM会将当前线程对应的CPU缓存及时的刷新到主内存中。
  • 每个volatile读取之前,插入一个 LoadLoad ,读取之后插入一个LoadStoreJMM会将对应的CPU缓存中的内存设置为无效,必须去主内存中重新读取共享变量
4.1.3.3 volatile原理总结

通过编译的字节码分析,我们可以知道,使用volatile 修饰的变量,编译后会生成ACC_VOLATILE关键字,通过关键字搜索,我们在hotspot 源码层(JVM层)查询到,is_volatile函数,这个函数的作用就是会先判断tos_type(volatile变量类型),后面有不同的基础类型的调用,比如int类型就调用release_int_field_put,byte就调用release_byte_field_put等等,还有就是调用了一个OrderAccess::storeload();函数,最终我们通过查看源码,找到storeload方法在不同CPU架构下的实现,最终基本可以得出以下结论:

  • 在JVM层:volitile的底层,在JVM层其实是通过内存屏障防止了指令重排序。
  • CPU层面:在x86的架构中,含有lock前缀的指令拥有两种方法实现;一种是开销很大的总线锁,它会把对应的总线直接全部锁住,如此明显是不合理的;所以后期intel引入了缓存锁以及mesi协议,如此便可以轻量化的实现内存屏障;

最终结论:volatile的底层原理,在JVM源码层次而言,内存屏障直接起到了禁止指令重排的作用,且之后与总线锁或者MESI协议配合实现了可见性;即:当写一个volatile变量,JMM会将当前线程对应的CPU缓存及时的刷新到主内存中;当读一个volatile变量,JMM会将对应的CPU缓存中的内存设置为无效,必须去主内存中重新读取共享变量。

4.2 synchronized

4.2.1 代码优化

package com.ningzhaosheng.thread.concurrency.features.visible.syn;/*** @author ningzhaosheng* @date 2024/2/5 19:52:31* @description 测试synchronized*/
public class TestSynchronized {private static boolean flag = true;public static void main(String[] args) throws InterruptedException {Thread t1 = new Thread(() -> {while (flag) {synchronized (TestSynchronized.class) {//...}System.out.println(111);}System.out.println("t1线程结束");});t1.start();Thread.sleep(10);flag = false;System.out.println("主线程将flag改为false");}
}

4.2.2 测试结果

从测试结果可以看出,使用了synchronized同步代码块之后,在主线程中修改了flag=false 之后,线程t1也获取到最新的变量值,结束了while循环。也就是说synchronized也可以解决并发编程的可见性问题。那么synchronized是怎么保证并发编程的可见性的呢,我们接下来分析下。

4.2.3 synchronized 原理分析

4.2.3.1 synchronized 修饰方法
4.2.3.1.1 源代码
package com.ningzhaosheng.thread.concurrency.features.visible.syn;/*** @author ningzhaosheng* @date 2024/2/13 10:16:36* @description synchronized 修饰方法*/
public class TestSynchronizedMethod {public static boolean flag = true;public static synchronized void runwhile() {while (flag) {System.out.println(111);}System.out.println("t1线程结束");}public static void main(String[] args) throws InterruptedException {Thread t1 = new Thread(() -> {runwhile();});t1.start();Thread.sleep(10);flag = false;System.out.println("主线程将flag改为false");}
}
4.2.3.1.2 执行结果

4.2.3.1.3 编译分析
javap -v .\TestSynchronizedMethod.class

可以看见,使用synchronized修饰方法后,通过javap -v 查看编译的字节码,会生成一个ACC_SYNCHRONIZED标识符,会隐式调用monitorenter和monitorexit。在执行同步方法前会调用monitorenter,在执行完同步方法后会调用monitorexit。

可查看官网解析:Chapter 2. The Structure of the Java Virtual Machine (oracle.com)

该标识符的作用是使当前线程优先获取Monitor对象,同一个时刻只能有一个线程获取到,在当前线程释放Monitor对象之前,其它线程无法获取到同一个Monitor对象,从而保证了同一时刻只能有一个线程进入到被synchornized修饰的方法

获取到锁资源之后,会将内部涉及到的变量从CPU缓存中移除,且要求线程必须去主内存中重新拿数据,在释放锁之后,会立即将CPU缓存中的数据同步到主内存。

注意:关于Monitor的更多底层实现原理,由于篇幅原因,这里先不分析,后续会出相关文章详细说明synchronized,这里只是就实现可见性原理做些说明。

4.2.3.2 synchronized 修饰代码块
4.2.3.2.1 源代码
package com.ningzhaosheng.thread.concurrency.features.visible.syn;/*** @author ningzhaosheng* @date 2024/2/13 10:48:02* @description synchronized 修饰代码块*/
public class TestSynchronizedCodeBlock {public static boolean flag = true;public static void runwhile() {while (flag) {synchronized (TestSynchronizedCodeBlock.class) {System.out.println(flag);}System.out.println(111);}System.out.println("t1线程结束");}public static void main(String[] args) throws InterruptedException {Thread t1 = new Thread(() -> {runwhile();});t1.start();Thread.sleep(10);flag = false;System.out.println("主线程将flag改为false");}
}
4.2.3.2.2 执行结果

4.2.3.2.3 编译分析
javap -v .\TestSynchronizedCodeBlock.class

可以看到,使用synchronized修饰代码块后,查看编译的字节码会发现再存取操作静态共享变量时,会插入monitorenter、monitorexit原语指令,关于这两个指令的说明,可查看文档:

Chapter 6. The Java Virtual Machine Instruction Set (oracle.com)

它实现可见性的原理和上一小节说明的那样,都是:

当前线程优先获取Monitor对象,同一个时刻只能有一个线程获取到,在当前线程释放Monitor对象之前,其它线程无法获取到同一个Monitor对象,从而保证了同一时刻只能有一个线程进入到被synchornized修饰的代码块。

获取到锁资源之后,会将内部涉及到的变量从CPU缓存中移除,且要求线程必须去主内存中重新拿数据,在释放锁之后,会立即将CPU缓存中的数据同步到主内存。

4.3 Lock

4.3.1 优化代码

package com.ningzhaosheng.thread.concurrency.features.visible.lock;import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;/*** @author ningzhaosheng* @date 2024/2/5 19:57:24* @description 测试Lock*/
public class TestLock {private static boolean flag = true;private static Lock lock = new ReentrantLock();public static void main(String[] args) throws InterruptedException {Thread t1 = new Thread(() -> {while (flag) {lock.lock();try {//...} finally {lock.unlock();}}System.out.println("t1线程结束");});t1.start();Thread.sleep(10);flag = false;System.out.println("主线程将flag改为false");}
}

4.3.2 测试结果

4.3.3 Lock实现可见性原理分析

4.3.3.1 源码分析

通过以上截图可以看到,我们创建了一个ReentrantLock,调用了它的lock()方法,而ReentrantLock实现了Lock接口和基于AQS定义实现了锁。

4.3.3.2 总结

Lock锁保证可见性的方式和synchronized完全不同,synchronized基于他的内存语义,在获取锁和释放锁时,对CPU缓存做一个同步到主内存的操作。

Lock锁是基于volatile实现的。Lock锁内部再进行加锁和释放锁时,会对一个由volatile修饰的state属性进行加减操作。

如果对volatile修饰的属性进行写操作,CPU会执行带有lock前缀的指令,CPU会将修改的数据,从CPU缓存立即同步到主内存,同时也会将其他的属性也立即同步到主内存中。还会将其他CPU缓存行中的这个数据设置为无效,必须重新从主内存中拉取。

参考4.1.3.3 volatile原理总结部分。

4.4 final

4.4.1 final 实现可见性原理分析

final修饰的属性,在运行期间是不允许修改的,这样一来,就间接的保证了可见性,所有多线程读取final属性,值肯定是一样。

final并不是说每次取数据从主内存读取,他没有这个必要,而且final和volatile是不允许同时修饰一个属性的。

final修饰的内容已经不允许再次被写了,而volatile是保证每次读写数据去主内存读取,并且volatile会影响一定的性能,就不需要同时修饰。

好了,本次内容就分享到这,欢迎关注本博主。如果有帮助到大家,欢迎大家点赞+关注+收藏,有疑问也欢迎大家评论留言!

相关文章:

Java 三大并大特性-可见性介绍(结合代码、分析源码)

目录 ​编辑 一、可见性概念 1.1 概念 二、可见性问题由来 2.1 由来分析 三、可见性代码例子 3.1 代码 3.2 执行结果 四、Java 中保证可见性的手段 4.1 volatile 4.1.1 优化代码 4.1.2 测试结果 4.1.3 volatile原理分析 4.1.3.1 查看字节码 4.1.3.2 hotspot 层面…...

【漏洞复现】狮子鱼CMS某SQL注入漏洞01

Nx01 产品简介 狮子鱼CMS(Content Management System)是一种网站管理系统,它旨在帮助用户更轻松地创建和管理网站。该系统拥有用户友好的界面和丰富的功能,包括页面管理、博客、新闻、产品展示等。通过简单直观的管理界面&#xf…...

《Java 简易速速上手小册》第6章:Java 并发编程(2024 最新版)

文章目录 6.1 线程的创建和管理 - 召唤你的士兵6.1.1 基础知识6.1.2 重点案例:实现一个简单的计数器6.1.3 拓展案例 1:定时器线程6.1.4 拓展案例 2:使用 Executor 框架管理线程 6.2 同步机制 - 维持军队的秩序6.2.1 基础知识6.2.2 重点案例&a…...

C++初阶:容器(Containers)list常用接口详解

介绍完了vector类的相关内容后,接下来进入新的篇章,容器list介绍: 文章目录 1.list的初步介绍2.list的定义(constructor)3.list迭代器( iterator )4.string的三种遍历4.1迭代器4.2范围for循环 5…...

HARRYPOTTER: FAWKES

攻击机 192.168.223.128 目标机192.168.223.143 主机发现 nmap -sP 192.168.223.0/24 端口扫描 nmap -sV -p- -A 192.168.223.143 开启了21 22 80 2222 9898 五个端口,其中21端口可以匿名FTP登录,好像有点说法,百度搜索一下发现可以用anonymous登录…...

嵌入式Qt 第一个Qt项目

一.创建Qt项目 打开Qt Creator 界面选择 New Project或者选择菜单栏 【文件】-【新建文件或项目】菜单项 弹出New Project对话框,选择Qt Widgets Application 选择【Choose】按钮,弹出如下对话框 设置项目名称和路径,按照向导进行下一步 选…...

【OpenHarmony硬件操作】风扇与温湿度模块

文章目录 前言一、串行通信是什么二、IC2.1 IC是什么2.2 IC涉及到的线2.3 IC的时序三、风扇的操作3.1 关于 pcf85743.2 风扇的接口函数IO拓展芯片的定义初始化PCF8574初始化 IO拓展版的引脚属性开启和关闭风扇读状态四、温湿度传感器的使用4.1 初始化温湿度传感器</...

Vue3.4+element-plus2.5 + Vite 搭建教程整理

一、 Vue3Vite 项目搭建 说明&#xff1a; Vue3 最新版本已经基于Vite构建&#xff0c;关于Vite简介&#xff1a;Vite 下一代的前端工具链&#xff0c;前端开发与构建工具-CSDN博客 1.安装 并 创建Vue3 应用 npm create vuelatest 创建过程可以一路 NO 目前推荐使用 Vue R…...

STM32Cubmax stm32f103zet6 SPI通讯

一、基本概念 SPI 是英语 Serial Peripheral interface 的缩写&#xff0c;顾名思义就是串行外围设备接口。是 Motorola 首先在其 MC68HCXX 系列处理器上定义的。 SPI 接口主要应用在 EEPROM&#xff0c; FLASH&#xff0c;实时时 钟&#xff0c; AD 转换器&#xff0c;还有数…...

每日OJ题_位运算⑤_力扣371. 两整数之和

目录 力扣371. 两整数之和 解析代码 力扣371. 两整数之和 371. 两整数之和 难度 简单 给你两个整数 a 和 b &#xff0c;不使用 运算符 和 - &#xff0c;计算并返回两整数之和。 示例 1&#xff1a; 输入&#xff1a;a 1, b 2 输出&#xff1a;3示例 2&#xff1a; …...

Mysql中索引优化和失效

什么是索引 要了解索引优化和索引失效的场景就要先了解什么是索引 索引是一种有序的存储结构&#xff0c;按照单个或者多个列的值进行排序&#xff0c;以提升搜索效率。 索引的类型 UNIQUE唯一索引 不可以出现相同的值&#xff0c;可以有NULL值。 INDEX普通索引 允许出现相同…...

使用Python+OpenCV2进行图片中的文字分割(支持竖版)

扣字和分割 把图片中的文字&#xff0c;识别出来&#xff0c;并将每个字的图片抠出来&#xff1b; import cv2 import numpy as npHIOG 50 VIOG 3 Position []水平投影 def getHProjection(image):hProjection np.zeros(image.shape,np.uint8)# 获取图像大小(h,w)image.sh…...

Qt中程序发布及常见问题

1、引言 当我们写好一个程序时通常需要发布给用户使用&#xff0c;那么在Qt中程序又是如何实现发布的呢&#xff0c;这里我就来浅谈一下qt中如何发布程序&#xff0c;以及发布程序时的常见问题。 2、发布过程 2.1、切换为release模式 当我们写qt程序时默认是debug模式&#x…...

C语言第二十三弹---指针(七)

✨个人主页&#xff1a; 熬夜学编程的小林 &#x1f497;系列专栏&#xff1a; 【C语言详解】 【数据结构详解】 指针 1、sizeof和strlen的对比 1.1、sizeof 1.2、strlen 1.3、sizeof 和 strlen的对比 2、数组和指针笔试题解析 2.1、⼀维数组 2.2、二维数组 总结 1、si…...

用HTML5 + JavaScript绘制花、树

用HTML5 JavaScript绘制花、树 <canvas>是一个可以使用脚本 (通常为JavaScript) 来绘制图形的 HTML 元素。 <canvas> 标签/元素只是图形容器&#xff0c;必须使用脚本来绘制图形。 HTML5 canvas 图形标签基础https://blog.csdn.net/cnds123/article/details/112…...

Science重磅_让大模型像婴儿一样学习语言

英文名称: Grounded language acquisition through the eyes and ears of a single child 中文名称: 通过一个孩子的眼睛和耳朵基于实践学习语言 文章: https://www.science.org/doi/10.1126/science.adi1374 代码: https://github.com/wkvong/multimodalbaby 作者: Wai Keen V…...

Java 数据结构篇-实现红黑树的核心方法

&#x1f525;博客主页&#xff1a; 【小扳_-CSDN博客】 ❤感谢大家点赞&#x1f44d;收藏⭐评论✍ 文章目录 1.0 红黑树的说明 2.0 红黑树的特性 3.0 红黑树的成员变量及其构造方法 4.0 实现红黑树的核心方法 4.1 红黑树内部类的核心方法 &#xff08;1&#xff09;判断当前…...

【实战】一、Jest 前端自动化测试框架基础入门(中) —— 前端要学的测试课 从Jest入门到TDD BDD双实战(二)

文章目录 一、Jest 前端自动化测试框架基础入门5.Jest 中的匹配器toBe 匹配器toEqual匹配器toBeNull匹配器toBeUndefined匹配器和toBeDefined匹配器toBeTruthy匹配器toBeFalsy匹配器数字相关的匹配器字符串相关的匹配器数组相关的匹配器异常情况的匹配器 6.Jest 命令行工具的使…...

【C语言 - 力扣 - 反转链表】

反转链表题目描述 给你单链表的头节点 head &#xff0c;请你反转链表&#xff0c;并返回反转后的链表。 题解1-迭代 假设链表为 1→2→3→∅&#xff0c;我们想要把它改成 ∅←1←2←3。 在遍历链表时&#xff0c;将当前节点的 next 指针改为指向前一个节点。由于节点没…...

ctfshow-php特性(web102-web115)

目录 web102 web103 web104 web105 web106 web107 web108 web109 web110 web111 web112 web113 web114 web115 实践是检验真理的 要多多尝试 web102 <?php highlight_file(__FILE__); $v1$_POST[V1]; $v2$_GET[v2]; $v3$_GET[v3]; $v4is_numeric($v2)and is…...

【Redis技术进阶之路】「原理分析系列开篇」分析客户端和服务端网络诵信交互实现(服务端执行命令请求的过程 - 初始化服务器)

服务端执行命令请求的过程 【专栏简介】【技术大纲】【专栏目标】【目标人群】1. Redis爱好者与社区成员2. 后端开发和系统架构师3. 计算机专业的本科生及研究生 初始化服务器1. 初始化服务器状态结构初始化RedisServer变量 2. 加载相关系统配置和用户配置参数定制化配置参数案…...

dedecms 织梦自定义表单留言增加ajax验证码功能

增加ajax功能模块&#xff0c;用户不点击提交按钮&#xff0c;只要输入框失去焦点&#xff0c;就会提前提示验证码是否正确。 一&#xff0c;模板上增加验证码 <input name"vdcode"id"vdcode" placeholder"请输入验证码" type"text&quo…...

srs linux

下载编译运行 git clone https:///ossrs/srs.git ./configure --h265on make 编译完成后即可启动SRS # 启动 ./objs/srs -c conf/srs.conf # 查看日志 tail -n 30 -f ./objs/srs.log 开放端口 默认RTMP接收推流端口是1935&#xff0c;SRS管理页面端口是8080&#xff0c;可…...

WordPress插件:AI多语言写作与智能配图、免费AI模型、SEO文章生成

厌倦手动写WordPress文章&#xff1f;AI自动生成&#xff0c;效率提升10倍&#xff01; 支持多语言、自动配图、定时发布&#xff0c;让内容创作更轻松&#xff01; AI内容生成 → 不想每天写文章&#xff1f;AI一键生成高质量内容&#xff01;多语言支持 → 跨境电商必备&am…...

12.找到字符串中所有字母异位词

&#x1f9e0; 题目解析 题目描述&#xff1a; 给定两个字符串 s 和 p&#xff0c;找出 s 中所有 p 的字母异位词的起始索引。 返回的答案以数组形式表示。 字母异位词定义&#xff1a; 若两个字符串包含的字符种类和出现次数完全相同&#xff0c;顺序无所谓&#xff0c;则互为…...

select、poll、epoll 与 Reactor 模式

在高并发网络编程领域&#xff0c;高效处理大量连接和 I/O 事件是系统性能的关键。select、poll、epoll 作为 I/O 多路复用技术的代表&#xff0c;以及基于它们实现的 Reactor 模式&#xff0c;为开发者提供了强大的工具。本文将深入探讨这些技术的底层原理、优缺点。​ 一、I…...

Rapidio门铃消息FIFO溢出机制

关于RapidIO门铃消息FIFO的溢出机制及其与中断抖动的关系&#xff0c;以下是深入解析&#xff1a; 门铃FIFO溢出的本质 在RapidIO系统中&#xff0c;门铃消息FIFO是硬件控制器内部的缓冲区&#xff0c;用于临时存储接收到的门铃消息&#xff08;Doorbell Message&#xff09;。…...

USB Over IP专用硬件的5个特点

USB over IP技术通过将USB协议数据封装在标准TCP/IP网络数据包中&#xff0c;从根本上改变了USB连接。这允许客户端通过局域网或广域网远程访问和控制物理连接到服务器的USB设备&#xff08;如专用硬件设备&#xff09;&#xff0c;从而消除了直接物理连接的需要。USB over IP的…...

SAP学习笔记 - 开发26 - 前端Fiori开发 OData V2 和 V4 的差异 (Deepseek整理)

上一章用到了V2 的概念&#xff0c;其实 Fiori当中还有 V4&#xff0c;咱们这一章来总结一下 V2 和 V4。 SAP学习笔记 - 开发25 - 前端Fiori开发 Remote OData Service(使用远端Odata服务)&#xff0c;代理中间件&#xff08;ui5-middleware-simpleproxy&#xff09;-CSDN博客…...

深度学习水论文:mamba+图像增强

&#x1f9c0;当前视觉领域对高效长序列建模需求激增&#xff0c;对Mamba图像增强这方向的研究自然也逐渐火热。原因在于其高效长程建模&#xff0c;以及动态计算优势&#xff0c;在图像质量提升和细节恢复方面有难以替代的作用。 &#x1f9c0;因此短时间内&#xff0c;就有不…...