JAVA集合篇--深入理解ConcurrentHashMap图解版
一、前言
在Java并发编程中,线程安全的Map实现一直是一个重要话题。虽然我们可以使用Collections.synchronizedMap()
或者HashTable
来获得线程安全的Map,但它们的性能在高并发场景下往往不尽人意。ConcurrentHashMap作为Java并发包中的重要组件,以其优雅的设计和出色的性能成为了并发编程的首选。
二、为什么需要ConcurrentHashMap
1.1 传统方案的问题
在ConcurrentHashMap出现之前,我们主要有以下几种线程安全的Map实现:
HashTable
// HashTable的put方法
public synchronized V put(K key, V value) {// 整个方法都被synchronized修饰// 所有操作都是串行的
}
Collections.synchronizedMap()
// SynchronizedMap的实现
public V put(K key, V value) {synchronized (mutex) { // mutex通常是thisreturn m.put(key, value);}
}
这两种方案的共同问题是:
- 粗粒度锁:整个Map只有一把锁,所有操作都需要竞争这把锁
- 读写冲突:读操作也需要获取锁,无法并发进行
- 性能瓶颈:在高并发场景下,锁竞争激烈,性能急剧下降
1.2 性能对比示例
public class MapPerformanceTest {private static final int THREAD_COUNT = 16;private static final int OPERATION_COUNT = 100_000;public static void main(String[] args) throws InterruptedException {// 测试HashTableMap<String, Integer> hashTable = new Hashtable<>();testConcurrentAccess("HashTable", hashTable);// 测试ConcurrentHashMapMap<String, Integer> concurrentMap = new ConcurrentHashMap<>();testConcurrentAccess("ConcurrentHashMap", concurrentMap);}private static void testConcurrentAccess(String mapType, Map<String, Integer> map) throws InterruptedException {CountDownLatch startLatch = new CountDownLatch(1);CountDownLatch endLatch = new CountDownLatch(THREAD_COUNT);long startTime = System.currentTimeMillis();for (int i = 0; i < THREAD_COUNT; i++) {new Thread(() -> {try {startLatch.await();for (int j = 0; j < OPERATION_COUNT; j++) {map.put("key" + j, j);map.get("key" + j);}} catch (InterruptedException e) {Thread.currentThread().interrupt();} finally {endLatch.countDown();}}).start();}startLatch.countDown();endLatch.await();long endTime = System.currentTimeMillis();System.out.println(mapType + " 耗时: " + (endTime - startTime) + "ms");}
}
典型测试结果:
- HashTable: 2500ms+
- ConcurrentHashMap: 800ms-
三、ConcurrentHashMap的演进历史
2.1 JDK 1.5-1.7:分段锁时代
在JDK 1.7及之前,ConcurrentHashMap采用**分段锁(Segment)**的设计:
// JDK 1.7 ConcurrentHashMap结构
static final class Segment<K,V> extends ReentrantLock implements Serializable {transient volatile HashEntry<K,V>[] table; // 该段的哈希表transient int count; // 该段的元素数量transient int modCount; // 修改次数transient int threshold; // 扩容阈值final float loadFactor; // 负载因子
}// HashEntry结构
static final class HashEntry<K,V> {final int hash;final K key;volatile V value;volatile HashEntry<K,V> next; // 链表指针
}
分段锁原理:
- 将整个Map分成多个Segment(默认16个)
- 每个Segment相当于一个小的HashMap,拥有独立的锁
- 不同Segment之间的操作可以并发进行
- 只有操作同一个Segment时才需要竞争锁
// JDK 1.7 put操作
public V put(K key, V value) {Segment<K,V> s;if (value == null)throw new NullPointerException();int hash = hash(key);// 根据hash值确定segmentint j = (hash >>> segmentShift) & segmentMask;if ((s = (Segment<K,V>)UNSAFE.getObject(segments, (j << SSHIFT) + SBASE)) == null)s = ensureSegment(j);return s.put(key, hash, value, false);
}
2.2 JDK 1.8+:CAS + synchronized的革新
JDK 1.8对ConcurrentHashMap进行了重大改革:
主要变化:
- 取消分段锁:改为对每个桶(数组元素)进行锁定
- 引入红黑树:链表长度超过8时转换为红黑树
- CAS操作:大量使用CAS操作减少锁的使用
- 锁粒度更细:锁的粒度从Segment级别降低到桶级别
// JDK 1.8+ 主要数据结构
transient volatile Node<K,V>[] table; // 主数组
private transient volatile Node<K,V>[] nextTable; // 扩容时的新数组
private transient volatile int sizeCtl; // 控制标识符// Node结构
static class Node<K,V> implements Map.Entry<K,V> {final int hash;final K key;volatile V val;volatile Node<K,V> next;
}// 红黑树节点
static final class TreeNode<K,V> extends Node<K,V> {TreeNode<K,V> parent;TreeNode<K,V> left;TreeNode<K,V> right;TreeNode<K,V> prev;boolean red;
}
四、扩容的区别
4.1、JDK1.7中的扩容
- 基于Segment:ConcurrentHashMap 是由多个 Segment 组成的,每个 Segment 中包含一个 HashMap。当某个 HashMap 达到扩容阈值时,单独为该 Segment 进行扩容,而不会影响其他的 Segment。
- 扩容过程:每个 Segment 维护自己的负载因子,当 Segment 中的元素超过阈值时,该Segment 的 HashMap 会扩容,整体的 ConcurrentHashMap 并不是一次性全部扩容。
4.2、JDK1.8中的扩容
- 全局扩容:ConcurrentHashMap 取消了 Segment,变成了一个全局数组(类似于HashMap),因此当 ConcurrentHashMap 中的任意一个元素超出阈值时,整个 ConcurrentHashMap 都会触发扩容。
- 基于CAS扩容:在扩容时,ConcurrentHashMap 采用了类似 HashMap 的方式,但通过 CAS操作 确保线程的安全,避免了锁住整个数组。在扩容时多个线程可以协同完成扩容。
- 渐进式扩容:JDK1.8的 ConcurrentHashMap 引入了渐进式扩容机制,扩容不是一次性将所有数据重新分配,而是多个线程共同参与,逐步迁移旧数据到新数组,降低了扩容时性能的开销
4.3、深入JDK 1.8:多线程协作扩容
// JDK 1.8 扩容核心流程
private final void transfer(Node<K,V>[] tab, Node<K,V>[] nextTab) {int n = tab.length, stride;// 计算每个线程的工作量if ((stride = (NCPU > 1) ? (n >>> 3) / NCPU : n) < MIN_TRANSFER_STRIDE)stride = MIN_TRANSFER_STRIDE;// 首个线程初始化新表if (nextTab == null) {try {Node<K,V>[] nt = (Node<K,V>[])new Node<?,?>[n << 1];nextTab = nt;} catch (Throwable ex) {sizeCtl = Integer.MAX_VALUE;return;}nextTable = nextTab;transferIndex = n;}int nextn = nextTab.length;ForwardingNode<K,V> fwd = new ForwardingNode<K,V>(nextTab);boolean advance = true;boolean finishing = false;// 多线程协作迁移for (int i = 0, bound = 0;;) {Node<K,V> f; int fh;// 获取工作范围while (advance) {int nextIndex, nextBound;if (--i >= bound || finishing)advance = false;else if ((nextIndex = transferIndex) <= 0) {i = -1;advance = false;}// CAS获取工作范围else if (U.compareAndSwapInt(this, TRANSFERINDEX, nextIndex,nextBound = (nextIndex > stride ?nextIndex - stride : 0))) {bound = nextBound;i = nextIndex - 1;advance = false;}}// 处理边界和收尾if (i < 0 || i >= n || i + n >= nextn) {// 扩容完成处理...}// 迁移具体的桶else if ((f = tabAt(tab, i)) == null)advance = casTabAt(tab, i, null, fwd);else if ((fh = f.hash) == MOVED)advance = true;else {synchronized (f) { // 锁定单个桶if (tabAt(tab, i) == f) {// 链表/红黑树迁移逻辑// 分为高位和低位两个链表/树}}}}
}// 多线程协作时序图
Thread-1 Thread-2 Thread-3| | || 发起扩容 | || 初始化nextTable | || transfer(0-15) | helpTransfer || | transfer(16-31) | helpTransfer| 迁移bucket-0 | | transfer(32-47)| 迁移bucket-1 | 迁移bucket-16 || ... | ... | 迁移bucket-32| 完成分配范围 | 完成分配范围 | ...| 检查是否全部完成 | 退出扩容 | 完成分配范围| 替换table引用 | | 退出扩容| 扩容完成 | |
优点:
- 多线程并发迁移,效率高
- 细粒度锁,减少阻塞时间
- 渐进式迁移,内存使用平滑
- 支持扩容期间的读操作
缺点:
- 实现复杂度高
- 需要额外的协调机制
- 内存开销较大(ForwardingNode等)
五、核心源码分析
3.1 初始化过程
// 构造函数
public ConcurrentHashMap(int initialCapacity, float loadFactor, int concurrencyLevel) {if (!(loadFactor > 0.0f) || initialCapacity < 0 || concurrencyLevel <= 0)throw new IllegalArgumentException();if (initialCapacity < concurrencyLevel)initialCapacity = concurrencyLevel;long size = (long)(1.0 + (long)initialCapacity / loadFactor);// 计算最接近size的2的幂次方int cap = (size >= (long)MAXIMUM_CAPACITY) ?MAXIMUM_CAPACITY : tableSizeFor((int)size);this.sizeCtl = cap; // 设置初始容量
}// 懒初始化
private final Node<K,V>[] initTable() {Node<K,V>[] tab; int sc;while ((tab = table) == null || tab.length == 0) {if ((sc = sizeCtl) < 0)Thread.yield(); // 其他线程正在初始化,让出CPUelse if (U.compareAndSwapInt(this, SIZECTL, sc, -1)) {// CAS成功,获得初始化权限try {if ((tab = table) == null || tab.length == 0) {int n = (sc > 0) ? sc : DEFAULT_CAPACITY;Node<K,V>[] nt = (Node<K,V>[])new Node<?,?>[n];table = tab = nt;sc = n - (n >>> 2); // 0.75 * n}} finally {sizeCtl = sc;}break;}}return tab;
}
3.2 put操作详解
public V put(K key, V value) {return putVal(key, value, false);
}final V putVal(K key, V value, boolean onlyIfAbsent) {if (key == null || value == null) throw new NullPointerException();int hash = spread(key.hashCode()); // 计算hash值int binCount = 0;for (Node<K,V>[] tab = table;;) {Node<K,V> f; int n, i, fh;// 情况1:表未初始化if (tab == null || (n = tab.length) == 0)tab = initTable();// 情况2:目标桶为空,直接CAS插入else if ((f = tabAt(tab, i = (n - 1) & hash)) == null) {if (casTabAt(tab, i, null, new Node<K,V>(hash, key, value, null)))break; // CAS成功,结束循环}// 情况3:正在扩容,协助扩容else if ((fh = f.hash) == MOVED)tab = helpTransfer(tab, f);// 情况4:桶不为空,需要同步操作else {V oldVal = null;synchronized (f) { // 锁定桶的头节点if (tabAt(tab, i) == f) { // 双重检查// 4.1 链表结构if (fh >= 0) {binCount = 1;for (Node<K,V> e = f;; ++binCount) {K ek;// 找到相同key,更新valueif (e.hash == hash &&((ek = e.key) == key || (ek != null && key.equals(ek)))) {oldVal = e.val;if (!onlyIfAbsent)e.val = value;break;}Node<K,V> pred = e;// 到达链表尾部,插入新节点if ((e = e.next) == null) {pred.next = new Node<K,V>(hash, key, value, null);break;}}}// 4.2 红黑树结构else if (f instanceof TreeBin) {Node<K,V> p;binCount = 2;if ((p = ((TreeBin<K,V>)f).putTreeVal(hash, key, value)) != null) {oldVal = p.val;if (!onlyIfAbsent)p.val = value;}}}}// 检查是否需要树化if (binCount != 0) {if (binCount >= TREEIFY_THRESHOLD)treeifyBin(tab, i); // 转换为红黑树if (oldVal != null)return oldVal;break;}}}addCount(1L, binCount); // 更新计数return null;
}
3.3 get操作详解
public V get(Object key) {Node<K,V>[] tab; Node<K,V> e, p; int n, eh; K ek;int h = spread(key.hashCode());if ((tab = table) != null && (n = tab.length) > 0 &&(e = tabAt(tab, (n - 1) & h)) != null) {// 直接命中头节点if ((eh = e.hash) == h) {if ((ek = e.key) == key || (ek != null && key.equals(ek)))return e.val;}// hash值为负数,特殊节点(红黑树或转发节点)else if (eh < 0)return (p = e.find(h, key)) != null ? p.val : null;// 遍历链表while ((e = e.next) != null) {if (e.hash == h &&((ek = e.key) == key || (ek != null && key.equals(ek))))return e.val;}}return null;
}
get操作的特点:
- 无锁读取:get操作不需要加锁,依赖volatile保证可见性
- 快速路径:优先检查头节点,大多数情况下可快速返回
- 支持并发扩容:即使在扩容过程中也能正确读取数据
相关文章:
JAVA集合篇--深入理解ConcurrentHashMap图解版
一、前言 在Java并发编程中,线程安全的Map实现一直是一个重要话题。虽然我们可以使用Collections.synchronizedMap()或者HashTable来获得线程安全的Map,但它们的性能在高并发场景下往往不尽人意。ConcurrentHashMap作为Java并发包中的重要组件࿰…...
Python嵌套循环
一、前言 在 Python 编程中,嵌套循环(Nested Loops) 是指在一个循环的内部再嵌套另一个循环。这种结构常用于处理多维数据结构(如二维数组、矩阵)、遍历组合数据、图形绘制等场景。 虽然嵌套循环在逻辑上更复杂&…...
linux编译安装nginx
1.到官网(nginx)下载nginx压缩包: 2.以(nginx-1.24.0.tar.gz)为例: 1.上传压缩包至linux服务器: rz 2.解压压缩包nginx-1.24.0.tar.gz: tar -zxvf nginx-1.24.0.tar.gz 3.在安装Nginx之前,需…...
算法-动态规划-钢条切割问题
钢条切割问题是一个经典的动态规划问题,旨在通过切割钢条获得最大收益。以下是详细解释和解决方案: 问题描述 给定长度为 n 的钢条和价格表 p,其中 p[i] 表示长度为 i 的钢条的价格(i 1, 2, ..., n)。目标ÿ…...
Java八股文——系统场景设计
如何设计一个秒杀场景? 面试官您好,设计一个秒杀系统,是对一个工程师综合技术能力的巨大考验。它的核心挑战在于,如何在极短的时间内,应对超高的并发请求,同时保证数据(尤其是库存)…...
如何在FastAPI中玩转GitHub认证,让用户一键登录?
title: 如何在FastAPI中玩转GitHub认证,让用户一键登录? date: 2025/06/22 09:11:47 updated: 2025/06/22 09:11:47 author: cmdragon excerpt: GitHub第三方认证集成通过OAuth2.0授权码流程实现,包含用户跳转GitHub认证、获取授权码、交换访问令牌及调用API获取用户信息四…...
[RPA] 影刀RPA实用技巧
1.给数字添加千分位分隔符 将变量variable的数值(2025.437)添加千分位分隔符,使其变为2,025.437 流程搭建: 关键指令: 2.删除网页元素 将bilibili官网的"动态"图标进行删除 流程搭建: 关键指令: 呈现效果…...
RA4M2开发IOT(7)----RA4M2驱动涂鸦CBU模组
RA4M2开发IOT.7--RA4M2驱动涂鸦CBU模组 概述视频教学样品申请硬件准备参考程序初始化 LSM6DSV16X 传感器初始化单双击识别主程序接口RA4M2接口生成UARTUART属性配置R_SCI_UART_Open()函数原型回调函数user_uart_callback0 ()变量定义更新敲击状态DP同步长按进入配网涂鸦协议解析…...
华为公布《鸿蒙编程语言白皮书》V1.0 版:解读适用场景
6 月 22 日消息,华为现已在其开发者网站上架《鸿蒙编程语言白皮书》V1.0 版本,主要围绕鸿蒙 HarmonyOS 整体框架、适用场景、演进策略、未来愿景四大角度进行阐述,文档访问地址(https://developer.huawei.com/consumer/cn/doc/gui…...
多源异构数据接入与实时分析:衡石科技的技术突破
在数字化转型的浪潮中,企业每天产生的数据量呈指数级增长。这些数据来自CRM系统、IoT设备、日志文件、社交媒体、交易平台等众多源头,格式各异、结构混乱、流速不一。传统的数据处理方式如同在无数孤立的岛屿间划着小船传递信息,效率低下且无…...
多设备Obsidian笔记同步:WebDAV与内网穿透技术高效实现教程
文章目录 前言1. Windows开启Webdav服务2. 客户端测试3. 安装Cpolar内网穿透实现公网访问Webdav4. 同步PC端笔记至WebDav4.1 首先需要在IIS中添加md的格式4.2 在Obsidian中安装第三方插件 5. 同步手机端笔记至WebDav 前言 各位好!在数字化浪潮席卷的当下࿰…...
Linux->进程概念(精讲)
引入:本文会讲到的东西有哪些? 注:要讲就讲清楚,所以从0到懂,目录在右侧 一:冯诺依曼体系结构 1:人物介绍 冯诺依曼是一个伟大的人,他提出了一个体系结构,被命名冯诺依…...
【舞蹈】PC-Dance:姿势可控的音乐驱动舞蹈合成
PC-Dance:姿势可控的音乐驱动舞蹈合成 自监督节奏对齐学习音乐到舞蹈的对齐嵌入-PC-Syn 中,依然怒了一种用于 自适应运动图构建(AMGC)的高效方案,可以基于图的优化效率并保持动作的多样性。 舞蹈合成 整体情况 我们的系统主要由音乐到舞蹈对齐嵌 入网络(M2D-Align)和姿势…...
uni-app项目实战笔记22--图片预览和切换
需求描述: 1、图片预览时,通常需要知道,当前预览的是第几张,总共有多少张图片; 2、当用户左右滑动切换预览图片时,当前预览索引需要随着进行切换。 下面简单介绍下实现过程: 1、在图片列表页…...
[特殊字符] AIGC工具深度实战:GPT与通义灵码如何彻底重构企业开发流程
🔍 第一模块:理念颠覆——为什么AIGC不是“玩具”而是“效能倍增器”? ▍企业开发的核心痛点图谱(2025版) 研发效能瓶颈:需求膨胀与交付时限矛盾持续尖锐,传统敏捷方法论已触天花板…...
华为OD机考-用户调度问题-DP(JAVA 2025B卷)
import java.util.Scanner;public class UserScheduling {public static void main(String[] args) {Scanner scanner new Scanner(System.in);int n scanner.nextInt(); // 用户个数int[][] costs new int[n][3]; // 存储每个用户使用A/B/C策略的系统消耗for (int i 0; i …...
【论文阅读 | CVPR 2024 |Fusion-Mamba :用于跨模态目标检测】
论文阅读 | CVPR 2024 |Fusion-Mamba :用于跨模态目标检测 1.摘要&&引言2.方法2.1 预备知识2.2 Fusion-Mamba2.2.1 架构特征提取与多模态融合(FMB模块)FMB的应用与输出2.2.2 关键组件3.2.2.1 SSCS 模块:浅层跨模态特征交互…...
Python 数据分析与可视化 Day 4 - Pandas 数据筛选与排序操作
🎯 今日目标 掌握 Pandas 中 groupby() 的使用方式学会使用 agg() 方法进行多个聚合掌握 pivot_table() 构建透视表结合分组与排序进行更深入的分析 🧮 一、基本分组统计(groupby) ✅ 分组 单列聚合 df.groupby("性别&qu…...
基于Vue.js的图书管理系统前端界面设计
一、系统前端界面设计要求与效果 (一)系统功能结构图 设计一个基于Vue.js的图书管理系统前端界面。要充分体现Vue的核心特性和应用场景,同时结合信息管理专业的知识。要求系统分为仪表盘、图书管理、借阅管理和用户管理四个主要模块&#x…...
FPGA故障注入测试软件使用指南
有数字芯片之母别称的FPGA,是国内在半导体行业率先取得重大突破的细分赛道,正迎来技术和市场形成共振的黄金发展期。 国内拥有最多的应用设计工程师与新兴从业人员,但到目前为止,还没有一款位流级别的专用EDA软件,服务用户日常应用开发所需的调试验证工作。 第一大厂商赛…...
Oracle 数据库查询:单表查询
作者:IvanCodes 日期:2025年6月22日 专栏:Oracle教程 在 Oracle 数据库操作中,查询数据是最频繁、最核心的操作之一。单表查询,即仅从一个表中检索信息,是所有复杂查询的基础。本笔记将系统梳理单表查询的关…...
【DDD】——带你领略领域驱动设计的独特魅力
🎼个人主页:【Y小夜】 😎作者简介:一位双非学校的大三学生,编程爱好者, 专注于基础和实战分享,欢迎私信咨询! 🎆入门专栏:🎇【MySQL࿰…...
阿里云CentOS系统搭建全攻略:开启云端技术之旅
前期准备:开启云端征程前的必备事项 在当今数字化时代,云计算已成为企业和开发者构建应用和服务的重要基础设施。阿里云作为全球领先的云计算服务提供商,提供了丰富的云计算产品和服务,其中 CentOS 系统在阿里云上的应用非常广泛…...
Flink图之间流转解析:从逻辑构建到物理执行的深度剖析
在Flink强大的数据处理体系中,Table Connectors实现了与外部结构化数据的高效交互,而Flink作业从代码到实际执行的背后,是各类图结构之间的流转与转换。这些图结构承载着作业的逻辑定义、任务划分与资源调度等关键信息,其流转过程…...
详解Redis数据库和缓存不一致的情况及解决方案
数据库与缓存不一致是分布式系统中常见问题,本质是数据在缓存层和存储层出现版本差异。 一、并发写操作导致不一致(最常见) 场景描述 线程A更新数据库 → 线程B更新数据库 → 线程B更新缓存 → 线程A更新缓存 结果:缓存中存储的…...
【CSS】CSS3媒体查询全攻略
媒体查询教程 媒体查询(Media Queries)是CSS3中引入的强大功能,允许内容根据设备特性(如屏幕尺寸、分辨率、方向等)进行自适应调整。以下是媒体查询的详细教程: 基本语法 media mediatype and (media feature) {/* CSS规则 */ }常用媒体类型 all - 所…...
深入理解Spring的ResponseBodyAdvice接口
什么是ResponseBodyAdvice? ResponseBodyAdvice是Spring框架4.2版本引入的一个非常有用的接口,它允许我们在控制器方法执行后、响应体写入前对响应进行统一处理。这个接口为开发者提供了对返回数据进行统一拦截和修改的能力,是Spring MVC响应处理流程中…...
C++法则5: 在函数调用过程中,具有非引用类型的参数要进行拷贝初始化。
C法则5: 在函数调用过程中,具有非引用类型的参数要进行拷贝初始化。 在 C 中,法则5指的是:当函数参数是非引用类型(即按值传递)时,传递给函数的实参会进行拷贝初始化(copy initializ…...
Python 使用 Requests 模块进行爬虫
目录 一、请求数据二、获取并解析数据四、保存数据1. 保存为 CSV 文件2. 保存为 Excel 文件打开网页图片并将其插入到 Excel 文件中 五、加密参数逆向分析1. 定位加密位置2. 断点调试分析3. 复制相关 js 加密代码,在本地进行调试(难)4. 获取 …...
day039-nginx配置补充
文章目录 0. 老男孩思想-如何提升能力?1. nginx登录认证功能1.1 创建密码文件1.2 修改子配置文件1.3 重启服务 2. nginx处理请求流程3. 配置默认站点4. location 命令5. 案例1-搭建大型直播购物网站5.1 配置本地hosts解析5.2 编写子配置文件5.3 创建相关目录/文件并…...
K8s入门指南:架构解析浓缩版与服务间调用实战演示
目录 前言一、k8s概念理解1、k8s整体架构(1) Master 主节点(2) Node 工作节点(3) Etcd 键值存储数据库 2、Pod被视为最小的部署单元3、k8s的五种控制器类型(1)…...
如何用AI开发完整的小程序<10>—总结
通过之前9节的学习。 如何用Ai制作一款简单小程序的内容就已经都介绍完了。 总结起来就以下几点: 1、搭建开发制作环境 2、创建页面(需要手动) 3、在页面上制作UI效果(让Ai搞,自己懂了后可以自己调) 4…...
Javaweb - 3 CSS
CSS 层叠样式表(Cascading Style Sheets),能够对网页中元素位置的排版进行像素级精确控制,支持几乎所有的字体字号样式,拥有对网页对象和模型样式编辑的能力。 简单来说,HTML 搭建一个毛坯房,C…...
【算法】【优选算法】优先级队列
目录 一、1046.最后一块石头的重量二、703. 数据流中的第 K 大元素三、692. 前 K 个⾼频单词四、295. 数据流的中位数 一、1046.最后一块石头的重量 题目链接:1046.最后一块石头的重量 题目描述: 题目解析: 题意就是让我们拿出提供的数组…...
PaddleOCR + Flask 构建 Web OCR 服务实战
1、前言 随着图像识别技术的发展,OCR(光学字符识别)已经成为很多应用场景中的基础能力。PaddleOCR 是百度开源的一个高性能 OCR 工具库,支持中英文、多语言、轻量级部署等特性。 而 Flask 是一个轻量级的 Python Web 框架,非常适合快速构建 RESTful API 或小型 Web 应用…...
openapi-generator-maven-plugin自动生成HTTP远程调用客户端
Java开发中调用http接口的时候,有很多可选的技术方案,比如:HttpURLConnection、RestTemplate、WebClient、Feign、Retrofit、Okhttp等,今天我们来看一个更优的技术方案OpenAPI Generator(http://openapi-generator.tech/) OpenAP…...
ms-swift 部分命令行参数说明
参考链接 命令行参数 — swift 3.6.0.dev0 文档 Qwen Chat num_train_epochs 训练的epoch数,默认为3 假设你有 1000 条训练样本,并且设置了: num_train_epochs 3 这意味着: 模型会完整地遍历这 1000 条数据 3 次。每一次…...
【学习笔记】深入理解Java虚拟机学习笔记——第10章 前端编译与优化
第10章 前端编译与优化 10.1 概述 1>前端编译器:Javac命令。 【.java文件->.class文件】 2>即时编译器:Hotspot.C1.C2 【.class文件->机器码】 3>提前编译器:JDK的Jaotc等【.java->机器码】 10.2 Javac 编译器 10.2.1 …...
删除node并且重装然后重装vue
参考第一篇文章 node.js卸载与安装超详细教程_node卸载重装-CSDN博客 第二篇文章安装vue Vue安装与配置教程(非常详细)_安装vue-CSDN博客...
Flink源码阅读环境准备全攻略:搭建高效探索的基石
想要深入探索Flink的底层原理,搭建一套完整且适配的源码阅读环境是必经之路。这不仅能让我们更清晰地剖析代码逻辑,还能在调试过程中精准定位关键环节。接下来,结合有道云笔记内容,从开发工具安装、源码获取导入到调试配置&#x…...
【破局痛点,赋能未来】领码 SPARK:铸就企业业务永续进化的智慧引擎—— 深度剖析持续演进之道,引领数字化新范式
摘要 在瞬息万变的数字时代,企业对业务连续性、敏捷创新及高效运营的需求日益迫切。领码 SPARK 融合平台,秉持“持续演进”这一核心理念,以 iPaaS 与 aPaaS 为双擎驱动,深度融合元数据驱动、智能端口调度、自动化灰度切换、AI 智…...
Flink SQL Connector Kafka 核心参数全解析与实战指南
Flink SQL Connector Kafka 是连接Flink SQL与Kafka的核心组件,通过将Kafka主题抽象为表结构,允许用户使用标准SQL语句完成数据读写操作。本文基于Apache Flink官方文档(2.0版本),系统梳理从表定义、参数配置到实战调优…...
Linux 服务器运维:磁盘管理与网络配置
🤵♂️ 个人主页:布说在见 ✍🏻作者简介: 🐋 希望大家多多支持,我们一起进步!😄 如果文章对你有帮助的话, 欢迎评论 💬点赞👍🏻 收藏…...
PyTorch 入门学习笔记
目录 1 张量 1)张量的初始化和属性 2)张量操作 3)使用 NumPy 进行桥接 2 torch.autograd 1)背景 2)在 PyTorch 中的使用 3)Autograd 的微分机制 4)计算图原理 3 神经网络 1ÿ…...
9大策略深度解析MySQL多表JOIN性能优化
一、多表JOIN的现实挑战 在实际开发中,MySQL多表JOIN场景主要源于两类场景: • 历史遗留系统:老代码中未严格遵循范式设计的SQL语句• 数据库迁移:从Oracle迁移至MySQL时保留的复杂关联查询 这类操作潜藏多重风险: …...
CSS 逐帧动画
CSS 逐帧动画实现指南 逐帧动画(frame-by-frame animation)是一种通过快速连续显示一系列静态图像来创造运动效果的技术。以下是使用CSS实现逐帧动画的几种方法。 1. 使用 steps() 计时函数 这是实现逐帧动画最常用的方法,通过animation-timing-function的steps(…...
UE5 游戏模板 —— ThirdPersonGame
UE5 游戏模板 —— ThirdPersonGame 前言一、初始化旋转控制参数1.参数一2.参数二3.参数三4.参数四 二、输入系统总结 前言 有了前面的铺垫,第三人称模板简直是手到擒来了,我们只需要注意一些初始化的变量是做什么的即可,因为UE的Character …...
java中关于异步转同步的一些解决方案的对比与思考。【spring mvc堵塞式】
文章目录 1、Spring MVC堵塞式编程中的技术方案a) 最简单的方案,使用 DeferredResult 代码如下,代码解读:最终控制台输出如下。用户收到的结果 b) 上点难度,使用redis监听事件,根据事件的不同返回不同的数据…...
【数据结构与算法】数据结构核心概念系统梳理
第一章 绪论:基础概念体系 🚩算法:问题求解步骤的描述。 🚩非递归的算法效率更高。 1.1 逻辑结构 vs 存储结构 维度逻辑结构存储结构(物理结构)定义数据元素之间的逻辑关系数据结构在计算机中的实现方式分类线性/树形/图/集合顺序/链式/索引/散列独立性独立于存储结构…...
《HTTP权威指南》 第7章 缓存
带着问题学习: 缓存如何提高性能如何衡量缓存的有效性缓存置于何处作用最大HTTP如何保持缓存副本的新鲜度缓存如何与其他缓存及服务器通信 web缓存是可以自动保存常见文档副本的HTTP设备。 缓存优点 减少冗余的数据传输,节省网络费用缓解网络瓶颈问题&…...