Blogs

Java 中的 ConcurrentHashMap 1.7 和 1.8 有什么区别

这两个版本的区别重点在锁的粒度

ConcurrentHashMap 1.7

Java 7 里使用的是分段锁 (Segment),底层依旧是数组,将数组分成 16 个 Segment,每个 Segment 下都有一个 HashMap 和 一个 ReentrantLock

不同线程访问不同 Segment 并不会触发锁,多线程访问同一个才会竞争,理论并发数默认 16

ConcurrentHashMap 1.8

Java 8 里去除了 Segment,采用跟 HashMap 相同的数据结构,数组 + 链表 + 红黑树 ,并且将 ReentrantLock 换成了 Synchronized

CAS

CAS 能够保证无锁操作下的原子性,具体是三个参数:地址(V)、原本值(A)、修改值(B)

  • 线程先读取内存地址(V)获取值,记录到原本值(A)里

  • 当线程准备更新地址(V)时,会先检查地址(V)当前值是否为记录的原本值(A)

    • 当前值和记录的原本值(A)相同,说明其他线程没有操作过,直接将地址值修改为修改值(B)

    • 当前值和记录的原本值(A)不同,说明其他线程修改过 V 的值,当前线程更新失败,通常尝试自旋或者放弃

1.8 插入

插入时采用 CAS 插入方法,冲突才会使用 Synchronized,但只对链表/红黑树的头节点上锁

因为锁住了头节点基本等于锁住了整个链表/红黑树,其他线程依旧可以对其他 bucket 进行操作,并发度大大提升

扩容的区别

Java 7 中的扩容
  • 基于 SegmentConcurrentHashMap 是由多个 Segment 组成的,每个 Segment 里面都包含着一个 HashMap,但 Segment 的数量并不会扩容,扩容的是 Segement 内的 HashMap,当 HashMap 达到扩容因子时,会单独为这个 Segment 进行扩容,并不会对整个 ConcurrentHashMap 进行扩容
  • 扩容过程:每个 Segment 维护自己的扩容因子,当 Segment 里面的元素超过阈值时对该 Segment 进行扩容,扩容过程跟 HashMap 一致,不会影响整个 ConcurrentHashMap
Java 8 中的扩容
  • 全局扩容:整个 ConcurrentHashMap 的元素总数超过阈值时,整个 ConcurrentHashMap 进行扩容
  • 基于CAS进行扩容ConcurrentHashMap 扩容跟 HashMap 基本一致,但是加上了 CAS 操作确保了线程安全。在扩容时,多个线程可以同时帮助扩容
  • 渐进式扩容:在 JDK 1.8 中引入了渐进式扩容,扩容时并不是一次将所有数据重新分配,而是多个线程共同参与,逐步迁移旧数据到新数组中,减低扩容开销。
    • 扩容时先把 transferIndex 待迁移的旧数组下标范围标记 设为旧数组长度,线程通过 CAS “抢占” 一段下标范围(比如从 transferIndex 取 16 个下标);
    • 线程迁移自己抢占的下标范围内的元素,迁移完后再 CAS 抢占新的范围,直到所有下标迁移完成;
    • “分段抢占、并行迁移”,避免单线程一次性迁移所有数据的开销。
    • 简单来说就算维护一个 transferIndex,线程循环 CAS 争夺下标,如果下标已经分配完了说明已经扩容完成

Java中的HashMap的原理

说白了 HashMap 底层就是一个数组,然后组合了链表红黑树

怎么寻址

当你存一个 Key-Value 的时候会计算出 keyhashcode,然后用哈希计算公式 keyhashcode % (table.length - 1) 算出需要存放的位置

处理哈希冲突

按照上面说的寻址我们会发现,有概率两个元素用公式算出来的结果是一样的,这就是我们常说的哈希冲突,HashMap 里也设定好了解决方法:

  • 链表:Java 7 及以前是把他们放在一起,用链表装着
    • 在 java 7 及以前链表使用的是头插法,即每次出现哈希冲突时,新的节点会插入到链表的头节点,老节点以此往后,在多线程环境下,头插法可能导致链表形成环,特别是在并发扩容时 rehashing 。当多个线程同时执行 put() 操作时,如果线程 A 正在进行头插,线程 B 也在同一时刻操作链表,可能导致链表结构出现环路,从而引发死循环,最终导致程序卡死或无限循环。
    • 但在 java 8 的时候改为尾插法,即新的节点插入到链表的尾部,保持链表的流畅度
  • 红黑树
    • Java 8 后做了优化,如果同一个下标的元素太多了(超过八个),则将链表转换成红黑树
    • 红黑树是一个自动平衡二叉树,能够将最坏情况下的查找复杂度从O(n) 变成 O(logn)
    • 如果数中的数量低于6,红黑树将会转换回链表,以减少不必要的开销

扩容机制

HashMap 默认长度是16,HashMap扩容因子(默认是0.75),意思是当元素超过 长度 * 0.75 时将会自动扩容。

默认长度是 16,扩容因子是 0.75,这个组合是性能和空间之间找到的平衡。如果扩容因子过高,虽然空间利用更高了,但是更容易出现哈希冲突,影响效率;如果扩容因子过低,哈希冲突出现概率减少,但是空间利用率低。

扩容就算将容量翻倍,然后把位置重新计算一遍,放进新的数组。

HashCode() 和 equals() 的重要性

HashMapkey 必须实现 HashCode()equals() 方法。

RAG

RAG 概念

什么是 RAG

RAG 是一种结合信息检索技术和 AI 内容生成的混合结构,可以解决大模型的知识时效性限制和幻读问题

简单来说就是让模型开卷考,让 AI 回答问题之前先查一下特定的知识库来获取信息,确保回答是基于资料而不是凭空捏造

RAG 和传统 AI 模型的区别

特性传统大语言模型RAG 增强模型
知识时效性受训练数据截止日期限制可接入最新知识库
领域专业性泛化知识⁠,专业深度有限可接入专业领域知识
响应准‌确性可能产生 “幻觉”基于检索的事​实依据
可控性依赖原始训练可通过知‎识库定制输出
资源消耗较高(需要大模型参‌数)模型可更小,结合外部知识

RAG 的工作流程

  • 文档收集和切割
  • 向量转换和存储
  • 文档过滤和检索
  • 检查增强和关联

文档收集和切割

文档收集:从各种来源(网页、PDF、数据库等)收集原始文档

Token

基础提示技巧

  • 明确指定任务和角色
    • 为 AI 提供清晰的任务描述和角色定位,帮助模型理解背景和期望
系统:你是一位经验丰富的 Java 教师,擅长向初学者解释编程概念。
用户:请解释 Java 中的列表推导式,包括基本语法和 2-3 个实用示例。
  • 提供详细说明和具体实例
    • 提供足够的上下文信息和期望的输出,减少模型的不确定性
请提供一个[计划],针对[项目],计划包括
1. ***
2. ***

示例格式:



  • 使用结构化格式引导思维
    • 通过列表、表格等格式,使指令更容易理解,输出更有条理
分析下面项目的优缺点
项目:****

请使用表格格式回答,包含下列:

  • 优势(最少三项)
  • 对每项优势的具体分析
  • 劣势(最少三项)
  • 对每项劣势的具体分析
  • 对应建议
  • 明确输出格式要求
    • 指定格式,长度,样式,使得输出更符合期望
  • 撰写一篇关于 Java 的学习路线推荐,要求:
    - 使用通俗易懂的语言,适合非专业阅读
    - 包含5个小标题,每个标题下2-3段文字
    - 总字数控制在800字左右
    - 结尾提供3个可行的个人行动建议
    

    进阶提示技巧

    • 思维链提示法(‌Chain-of-​Thought)
      • 引导模型展示推导过程,逐步思考问题,提高复杂问题的准确性
    问题:一个商店售卖T恤,每件15元。如果购买5件以上可以享受8折优惠。小明买了7件T恤,他需要支付多少钱?
    

    请一步步思考解决这个问题:

    1. 首先计算7件T恤的原价
    2. 确定是否符合折扣条件
    3. 如果符合,计算折扣后的价格
    4. 得出最终支付金额
    • 少样本学习(F‌ew-Shot L​earning)
      • 提供几个样本,帮助模型理解任务模式和期望输出
    我将给你几个代码,请你按照相同的方式解析错误所在
    

    输入:“system.out.print("hello world");” 输出:“错误所在:system,应该修改为 System,因为是调用 System 库”

    • 分步骤指导(Step-by-Step)
      • 将复杂问题分解为可管理的步骤,确保模型完成的每个环节正确
    请帮我创建一个简单的网站落地页设计方案,按照以下步骤:
    

    步骤1: 分析目标受众(考虑年龄、职业、需求等因素) 步骤2: 确定页面核心信息(主标题、副标题、价值主张) 步骤3: 设计页面结构(至少包含哪些区块) 步骤4: 制定视觉引导策略(颜色、图像建议) 步骤5: 设计行动召唤(CTA)按钮和文案

    • 自我评估和修正
      • 让模型评估自己的输出并进行改进,提高准确性和质量
    解决以下概率问题:
    从一副标准扑克牌中随机抽取两张牌,求抽到至少一张红桃的概率。
    

    首先给出你的解答,然后:

    1. 检查你的推理过程是否存在逻辑错误
    2. 验证你使用的概率公式是否正确
    3. 检查计算步骤是否有误
    4. 如果发现任何问题,提供修正后的解答
    • 知识检索和引用
      • 引导模型检索相关信息并明确引用信息来源,提高可靠性
    请讲解 SpringBoot 在执行的时候的启动流程。在回答中:
    1. 提供 SpringBoot 的官方解释
    2. 给出分别是哪几个组件
    3. 描述执行的先后顺序
    

    对于任何可能需要具体数据或研究支持的陈述,请明确指出这些信息的来源,并说明这些信息的可靠性。

    • 多视角分析
      • 引导模型从不同角度、立场或专业视角分析问题,提供全面见解
    分析"城市应该禁止私家车进入市中心"这一提议:
    

    请从以下4个不同角度分析:

    1. 环保专家视角
    2. 经济学家视角
    3. 市中心商户视角
    4. 通勤居民视角

    对每个视角:

    • 提供支持该提议的2个论点
    • 提供反对该提议的2个论点
    • 分析可能的折中方案
    • 多模态思维
      • 结合不同表⁠达形式进行思考,如‌文字描述、图表结构​、代码逻辑等
    • 设计一个智能家居系统的基础架构:
      
      1. 首先用文字描述系统的主要功能和组件
      2. 然后创建一个系统架构图(用ASCII或文本形式表示)
      3. 接着提供用户交互流程
      4. 最后简述实现这个系统可能面临的技术挑战

      尝试从不同角度思考:功能性、用户体验、技术实现、安全性等。

      超级智能体

      后端项目初始化

      环境准备

      安装的 JDK 版本必须是 21 以上,最好是 21 ,因为支持使用虚拟线程

      **不能使用其他版本

      新建项目

      在 IDEA 中新建项目,选择 Spring Boot 模板,注意需要确保 Server URL 为  https://start.spring.io/

      ![[Pasted image 20251106194511.png]]