📝 前言:为什么你需要了解 ThreadLocal?

        在多线程并发编程中,线程安全始终是一个绕不开的话题。我们常常需要为每个线程维护一份独立的上下文数据,例如用户信息、事务 ID、日志追踪 ID 等。这些数据不能被多个线程共享,否则会导致数据错乱或线程安全问题。

        Java 提供了一个非常优雅的工具类 —— ThreadLocal,它允许我们为每个线程绑定一个线程私有的变量副本,从而实现线程隔离、避免共享带来的并发问题。

        但其底层实现 ThreadLocalMap 的机制却并不简单,涉及到弱引用、哈希冲突处理、内存泄漏、清理机制、扩容策略等多个核心知识点。

先讲结论,后解释,因为我自己看javaguide的时候观感就是这里一坨那里一坨的,对结论不是很清晰,导致读者自己有一些理解,看javaguide的时候又有一些理解,对结论的记忆就不是很清晰):

但其底层实现 ThreadLocalMap 的机制却并不简单,涉及到弱引用、哈希冲突处理、内存泄漏、清理机制、扩容策略等多个核心知识点。

为了帮助你快速掌握重点,我先总结 ThreadLocal 的核心结论如下:

ThreadLocal 的核心结论

先说 ThreadLocal 的核心结论总结,供你快速掌握重点:

🧠 1. ThreadLocalMapThreadLocal 的静态内部类

  • ThreadLocalMapThreadLocal静态内部类

  • 包私有,无法通过外部直接访问,只能通过 ThreadLocal.get()set() 等方法间接访问。

  • 每个线程拥有自己的 ThreadLocalMap,所有 ThreadLocal 实例在该线程中都会映射到这个唯一的 Map


🔑 2. ThreadLocalMap 中的 Key 是弱引用

  • ThreadLocalMap 中的键值对结构为 Entry extends WeakReference<ThreadLocal<?>>

  • Key 是 ThreadLocal 实例的弱引用,Value 是强引用。

  • 这意味着:当外部没有强引用指向某个 ThreadLocal 实例时,该实例可能被 GC 回收,此时 Entry.key 会被置为 null

  • 不是 ThreadLocal 本身是弱引用,而是 ThreadLocalMap.Entry 的 key 是弱引用。


🧮 3. ThreadLocalMap 的 Hash 算法

  • 索引计算方式index = key.threadLocalHashCode & (len - 1),与 HashMap 类似。

  • 不同点在于:

    • ThreadLocalthreadLocalHashCode 是全局唯一的,由原子递增计数器生成。

    • 初始值为 0,每次递增 0x61c88647(斐波那契数,有助于哈希分布更均匀)。

    • HashMap 的哈希值依赖于键对象的 hashCode() 方法。


🧱 4. 数据结构与冲突处理

  • ThreadLocalMap 使用数组结构,不使用链表。

  • Hash 冲突解决方式:采用线性探测法(开放寻址法)

  • 如果计算的索引位置被占用,则向后查找空槽插入。


🧹 5. Null Key 的清理机制

  • 探测式清理(expungeStaleEntry)

    • 从某个失效 Entry 出发,向后清理所有连续的 null Key Entry

    • 同时重新哈希有效的 Entry。

  • 启发式清理(cleanSomeSlots)

    • set() 操作后触发,随机清理 log2(N) 个槽位

    • 防止内存泄漏扩散。

  • Set 操作时也会顺带清理一部分 Entry,清理范围有限,从当前位置向后清理。类似于“贴羊肉包子的时候顺便清理锅边的渣渣”。

  • Get 操作中如果遇到 Key 为 null 的 Entry,也会触发探测式清理

  • 扩容时(rehash() / resize())会进行全局清理。(类似于“如果要从一个做羊肉包的小窑的时候换到大窑的时候,可以有空一次性清理全部 key 为 null 的 entry”。)


🔁 6. 扩容机制

方法触发条件清理范围行为说明
rehash()size >= 2/3 * len全局清理清理 null Key Entry(将无效的清理)
resize()rehash 后仍 size >= 0.75 * len全局清理 + 扩容扩容为 2 倍,并将有效 Entry 重新哈希放入新表(将有效的拿出来)

📦 7. set() 和 get() 的原理

  • set()

    • 计算索引 → 线性探测 → 插入或覆盖 → 清理 null Entry → 扩容判断

  • get()

    • 计算索引 → 若 key 不匹配 → 线性探测查找 → 若发现 null key → 启动探测式清理

ThreadLocalMap 的常见问题解析

问题1:Entry的key-ThreadLocal<?> k 为什么要设计成弱引用?

原因: 设计成弱引用的原因;

  1. 内存泄漏的风险

    • 当一个 ThreadLocal 实例不再被任何强引用指向时(例如,用户代码中已经没有对该 ThreadLocal 的引用),理论上它应该被垃圾回收。

    • 但如果 ThreadLocalMap 的 key 是强引用,那么即使外部已经没有对该 ThreadLocal 的引用,ThreadLocalMap 仍然持有它的强引用,导致它永远无法被回收。

    • 这样就会造成 ThreadLocal 实例和对应的 value 都无法被释放,从而引发内存泄漏。

  2. 弱引用避免了这个问题

    • 如果 key 是弱引用,当外部没有强引用指向某个 ThreadLocal 实例时,垃圾回收器会在下一次 GC 时回收该 ThreadLocal 实例。

    • 此时,ThreadLocalMap 中对应的 key 会变成 null,但 value 仍然存在。ThreadLocalMap的清理机制 会在某些时机(如插入新条目时)清理这些 key 为 null 的条目,从而释放 value 的内存。(也就是说弱引用可以尽量处理这个内存泄漏的问题,但是不能完全解决,强引用是直接没办法。完全解决的办法,当然是直接remove整个entry,弱引用是保底措施。

问题2:当发生 GC 后,ThreadLocalMap 中的 key 是否为 null

在使用 ThreadLocal 时,一个常见问题是:当发生 GC 后,ThreadLocalMap 中的 key 是否为 null。

以下是所有可能的情况分析:

1.无外部强引用;

2.有外部强引用;

3.线程池复用;

4.ThreadLocal被重新赋值(这个就是改变了引用,其实可以当做无外部引用);

5.线程销毁。

✅ 场景 1:无外部强引用(常见内存泄漏场景)

描述:
ThreadLocal 实例未被任何强引用持有,如局部变量使用后未清理。

GC 后的状态:

  • Key 为 null(被回收)
  • Value 仍存在(内存泄漏)

代码示例:

ThreadLocal<String> local = new ThreadLocal<>();
local.set("value");
local = null;
System.gc();

解决方案:

  • 调用 remove() 显式清理
  • 使用 try-finally 确保清理

✅ 场景 2:有外部强引用

描述:
ThreadLocal 被静态变量或成员变量引用。

GC 后的状态:

  • Key 不为 null(未被回收)
  • Value 正常保留(无泄漏)

代码示例:

static ThreadLocal<String> local = new ThreadLocal<>();
local.set("value");
System.gc();

解决方案:

  • 无需处理,只要强引用存在,GC 不会回收

✅ 场景 3:线程池复用线程

描述:

  • 如果使用 线程池,线程会被复用,ThreadLocal 的 Value 可能残留。

  • 如果 ThreadLocal 被回收,但线程未销毁,ThreadLocalMap 会积累大量 Entry,其中 Key 为 null,Value 无法回收。

  • 类似酒店给你的房间,房间复用了,但是没有打扫卫生。

GC 后的状态:

  • Key 可能为 nullThreadLocal 被回收)
  • Value 仍存在(内存泄漏)
  • 长期运行可能导致 OOM

解决方案:

  • 任务结束时调用 remove()
  • 使用 ThreadPoolExecutor.afterExecute() 钩子清理

✅ 场景 4:ThreadLocal 被替换(重新赋值)

描述:
ThreadLocal 被重新赋值为新实例,旧实例无强引用。

GC 后的状态:

  • 旧 Key 为 null
  • 旧 Value 仍存在(内存泄漏)

代码示例:

ThreadLocal<String> local = new ThreadLocal<>();
local.set("old value");
local = new ThreadLocal<>(); // 旧对象无强引用
System.gc();
// ThreadLocalMap 中的 Entry:
//   Key: null(旧的 ThreadLocal 被回收)
//   Value: "old value"(内存泄漏)

解决方案:

  • 先调用 remove() 再赋值

✅ 场景 5:线程销毁

描述:

  • 线程销毁 时,ThreadLocalMap 会被回收,所有 Entry(包括 Key 和 Value)都会被 GC 清理。

  • 但如果使用 线程池,线程不会销毁,ThreadLocal 的内存泄漏问题仍然存在。

GC 后的状态:

  • ThreadLocalMap 被回收
  • 所有 Entry 被清理
  • Key 与 Value 都被释放

解决方案:

  • 无需手动清理
  • 但线程池场景不适用此机制
✅ 最佳实践
  1. 用完 ThreadLocal 必须调用 remove(),避免内存泄漏

    • 因为 Entry 的 key 是弱引用,value 是强引用
    • GC 后 key 会为 null,但 value 仍存在,造成内存泄漏
    • 推荐使用 try-finally 确保清理
  2. 避免在线程池中残留 ThreadLocal,使用 try-finallyafterExecute 清理

    • 线程池中线程是复用的,不会自动销毁 ThreadLocalMap
    • 若不清除,Entry 会持续累积,可能导致 OOM
    • 可使用 ThreadPoolExecutor.afterExecute() 统一清理
  3. 尽量使用 static final ThreadLocal,或至少 final ThreadLocal,避免被意外回收或替换。(场景1或者场景4)

    • final 表示引用不可变,防止被置 null 或重新赋值
    • 避免因弱引用导致 Key 丢失,即使你知道 value 是什么,也无法访问
    • static 更适合全局上下文,如用户信息、事务 ID 等
    • 如果只是对象内部状态,非 static 的 final ThreadLocal 也足够

问题3:ThreadLocalMap的set方法:

🔍 一、整体流程概览
private void set(ThreadLocal<?> key, Object value) {Entry[] tab = table;int len = tab.length;// 1.计算哈希槽int i = key.threadLocalHashCode & (len - 1); // 2.线性探测查找插入的位置for (Entry e = tab[i]; e != null; e = tab[i = nextIndex(i, len)]) {ThreadLocal<?> k = e.get();// 3.处理keyif (k == key) { // 3.1如果 key 已存在,直接覆盖 valuee.value = value;return;}if (k == null) { // 3.2如果 key=null(ThreadLocal 被 GC),替换旧 EntryreplaceStaleEntry(key, value, i);return;}}// 4.如果没有冲突,直接存入新 Entrytab[i] = new Entry(key, value);int sz = ++size;// 5.启发式清理 + 扩容判断if (!cleanSomeSlots(i, sz) && sz >= threshold) {rehash(); // 重新哈希并且扩容}
}

🧱 二、详细流程说明
✅ 1. 计算哈希槽
int i = key.threadLocalHashCode & (len - 1);
  • key.threadLocalHashCode 是一个全局递增的哈希值。
  • 初始值为 0,每次递增 0x61c88647(一个斐波那契数的十六进制值,用于均匀分布哈希值)。
  • len 是 Entry[] table 的长度(2 的幂)。
  • 使用位运算 & (len - 1) 模拟取模,提升效率。

作用:确定插入位置,减少哈希冲突。


✅ 2. 线性探测(开放寻址法)
for (Entry e = tab[i]; e != null; e = tab[i = nextIndex(i, len)]) {// ...
}
  • 如果当前槽位不为空(发生哈希冲突),则向后查找空槽(nextIndex())。
  • nextIndex(i, len):向后移动一位,如果越界则从数组头部开始(循环数组)。
  • 这种方式称为 开放寻址法,与 HashMap 的链表法不同。

作用:解决哈希冲突,寻找合适插入位置。


✅ 3. 处理 Key 冲突或 Null Key

在循环中会遇到以下几种情况:

🟢 情况 1:Key 相同(直接覆盖)

if (k == key) {e.value = value;return;
}
  • 如果当前 Entry 的 key 与插入 key 相同,直接更新 value。

🟡 情况 2:Key 为 null(ThreadLocal 被 GC)

if (k == null) {replaceStaleEntry(key, value, i);return;
}
  • 表示该 Entry 的 key 已被 GC 回收。
  • 调用 replaceStaleEntry() 替换并清理该 Entry。
  • 该方法内部会调用 expungeStaleEntry(),进行 探测式清理,清除连续的 null key Entry。

作用:避免内存泄漏,及时清理无效 Entry。


✅ 4. 插入新 Entry
tab[i] = new Entry(key, value);
int sz = ++size;
  • 如果找到空槽位,直接插入新的 Entry。
  • 更新 size(Entry 数量)。

✅ 5. 启发式清理 + 扩容判断
if (!cleanSomeSlots(i, sz) && sz >= threshold) {rehash();
}
private void rehash() {expungeStaleEntries();// Use lower threshold for doubling to avoid hysteresisif (size >= threshold - threshold / 4)resize();}

🟢 cleanSomeSlots(i, sz)启发式清理

  • 从当前索引开始,随机清理 log₂(size) 个槽位。
  • 如果清理了至少一个 null key Entry,返回 true

🟡 rehash()全局清理 + 扩容判断

  • 调用 expungeStaleEntries() 清理所有 null key Entry。
  • 如果清理后仍超过扩容阈值(threshold = len * 2/3),则调用 resize() 扩容。
  • 扩容为原来的 2 倍,并重新哈希所有的有效 Entry。

作用:控制内存使用,避免 OOM,提升性能。

问题4: ThreadLocalMap的清理机制

1. .set()方法清理机制的思维导图

2. 不同场景下的 set() 行为
(1) 最佳情况:槽位空闲(无冲突,无失效Entry)
  • 操作:直接插入新 Entry。

  • 附加清理:触发 启发式清理(cleanSomeSlots),以对数复杂度(log2(N))的步长扫描部分槽位,清理可能的失效 Entry。

  • 目的:预防性清理,减少未来内存泄漏风险。

(2) 哈希冲突:槽位被有效Entry占用(Key不匹配)
  • 操作:线性探测下一个槽位(i = nextIndex(i, len))。

  • 附加清理:无立即清理,但后续插入可能触发清理。

(3) 发现失效Entry(Key=null)
  • 操作:触发 替换式清理(replaceStaleEntry),分为以下步骤:

    1. 向前扫描: 从当前槽位向前遍历,找到 最早的失效 Entry 位置(slotToExpunge)

      for (i = prevIndex(staleSlot, len); (e = tab[i]) != null; i = prevIndex(i, len)) {if (e.get() == null) slotToExpunge = i;
      }
    2. 向后扫描: 从当前槽位向后遍历,处理两种情况:

      • 找到相同 Key:替换值并调整位置。

      • 其他失效 Entry:扩展清理范围。

    3. 探测式清理(expungeStaleEntry): 从 slotToExpunge 开始,向后清理连续失效 Entry,并重新哈希有效 Entry。

      private int expungeStaleEntry(int staleSlot) {// 清理当前槽位tab[staleSlot].value = null;tab[staleSlot] = null;size--;
      ​// 向后遍历清理for (i = nextIndex(staleSlot, len); (e = tab[i]) != null; i = nextIndex(i, len)) {ThreadLocal<?> k = e.get();if (k == null) {// 清理失效 Entry} else {// 重新哈希有效 Entry}}return i; // 返回第一个 null 的位置
      }


3. 关键清理方法对比
方法触发条件清理范围时间复杂度
启发式清理cleanSomeSlots()插入新 Entry 后触发随机扫描 log2(N) 个槽位O(log N)
替换式清理replaceStaleEntry()遇到失效 Entry 时触发向前找到链头 + 向后连续清理O(n)
探测式清理expungeStaleEntry()replaceStaleEntry() 内调用清理连续失效 EntryO(n)

4. 设计思想总结
  1. 乐观插入:优先保证插入效率,仅在必要时触发清理。

  2. 惰性清理:不完全依赖 set() 清理,需手动 remove() 确保安全。

  3. 局部整理:通过重新哈希有效 Entry,减少后续操作冲突概率。

  4. 内存安全:弱引用 Key 防止内存泄漏,但需配合清理机制。

5.清理方式总结
清理方式触发条件调用的清理方法清理范围是否完全清理
set() 探测式清理遇到 key=nullEntryreplaceStaleEntry() 替换式清理 + expungeStaleEntry()探测式,比下面了多了一段向前扫描局部(探测路径)遇到一个空槽就停止!
get() 惰性清理遇到 key=nullEntryexpungeStaleEntry()局部(探测路径)
rehash() 全局清理size >= thresholdexpungeStaleEntries(),清理全部无效的。全局(遍历所有位置)+局部探测⚠️(接近完全);我觉得是完全✅
resize() 完全清理rehash() 后仍需扩容只保留有效到新的容器。全局(遍历全部位置+重建新表)

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

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

相关文章

AD一张原理图分成多张原理图

先选中你要作为主级原理图的那张原理图&#xff0c;我这里是Sheet1,点击设计&#xff08;D&#xff09;&#xff0c;再点击create sheet symbol from sheet&#xff08;这个不能选错&#xff09;快捷键&#xff1a;DY弹窗中选择要加入的次级原理图左击鼠标放置重复上面的动作&a…

AI大模型资源

网址 https://sass.kupepoem.cn/ 登录-选择模型 打开上述网址后如下操作&#xff1a; 进入后&#xff0c;所有模型均可使用&#xff1a; o4-mini&#xff08;支持Deep Research深入研究&#xff09; 选择o4-mini模型 选择深度研究 要研究什么&#xff0c;对话直接输入即可。…

论文阅读--《Besting the Black-Box: Barrier Zones for Adversarial Example Defense》

好的&#xff0c;这是《Besting the Black-Box: Barrier Zones for Adversarial Example Defense》论文的中文翻译&#xff1a;[文件名称]: Besting_the_Black-Box_Barrier_Zones_for_Adversarial_Example_Defense.pdf [文件内容开始]第 1 页 收稿日期&#xff1a;2021年10月1…

如何保证GPFS文件系统的强一致性

一、底层机制&#xff1a;分布式锁与元数据管理GPFS 通过分布式锁和集中式元数据管理的结合&#xff0c;确保数据和元数据的一致性&#xff1a;集中式元数据服务器&#xff08;MDS&#xff09;GPFS 采用主从架构的元数据管理&#xff0c;由指定节点&#xff08;或集群&#xff…

使用Docker+Nginx部署电商平台项目(服务端+管理端+商城)

1.项目背景&#xff1a; 本项目作为小商场系统&#xff0c;涵盖内容如下&#xff1a; litemall Spring Boot后端 Vue管理员前端 微信小程序用户前端 Vue用户移动端 1.1项目架构&#xff1a; 1.2项目技术栈&#xff1a; 本项目可以使用window运行jar包本地部署&#xff0c…

Java网络编程入门:从基础原理到实践(二)

目录 1. 网络编程基础&#xff1a;搞懂设备通信的底层逻辑 1.1 为啥需要网络编程&#xff1f;—— 让设备 “互通有无” 1.2 什么是网络编程&#xff1f;—— 给数据 “定规矩、找路线” 1.3 网络编程的基本概念&#xff1a;理清通信里的角色和流程 1.3.1 发送端和接收端 …

XSS内容分享

反射型XSS &#xff1a;反射型XSS 是非持久性、参数型的跨站脚本。反射型XSS 的JS 代码在Web 应用的参数&#xff08;变量&#xff09;中&#xff0c;如搜索框的反射型XSS。在搜索框中&#xff0c;提交PoC[scriptalert(/xss/)/script]&#xff0c;点击搜索&#xff0c;即可触发…

电线杆距离居民区的安全距离【重要!!!】

10kV架空电线安全距离购房指南 中国大陆地区10kV架空电线距居民住宅需要满足1.5米水平安全距离&#xff08;裸导线&#xff09;和6.5米垂直安全距离的国家强制标准。根据现行法规&#xff0c;10kV系统的电磁辐射水平极低&#xff0c;对居民健康影响可忽略不计&#xff0c;但购房…

河南萌新联赛2025第(二)场:河南农业大学

我看到花儿在绽放 我听到鸟儿在歌唱 我看到人们匆匆忙忙 我看到云朵在天上 我听到小河在流淌 我看到人们漫步在路上 河南萌新联赛2025第&#xff08;二&#xff09;场&#xff1a;河南农业大学 河南萌新联赛2025第&#xff08;二&#xff09;场&#xff1a;河南农业大学_ACM/N…

unixbench系统性能测试

unixbench系统性能测试 环境&#xff1a; UnixBench: 6.0.0(2025-05-21)简介 UnixBench 是一款经典的 Unix/Linux 系统性能测试工具&#xff0c;主要用于评估系统的CPU 运算能力、内存性能、多线程处理能力以及部分系统调用&#xff08;如进程创建、文件操作&#xff09;的效率…

上线了,自己开发的刷题小程序,vue3.0

嘿&#xff0c;最近我搞了个Java刷题的小程序&#xff0c;用Vue写的&#xff0c;界面和功能都还挺完整的。今天就来跟大家聊聊这个小程序是怎么实现的&#xff0c;代码里都藏着哪些小细节。 先看整体结构&#xff0c;我把整个页面分成了几个大块&#xff1a;顶部导航栏、题目内…

嵌入式开发学习———Linux环境下数据结构学习(三)

单向循环链表单向循环链表是一种特殊的单向链表&#xff0c;尾节点的指针指向头节点&#xff0c;形成一个闭环。适用于需要循环访问的场景&#xff0c;如轮询调度。结构特点&#xff1a;每个节点包含数据域和指向下一个节点的指针&#xff0c;尾节点的指针指向头节点而非空值。…

【华为机试】684. 冗余连接

文章目录684. 冗余连接描述示例 1示例 2提示解题思路核心分析问题转化算法选择策略1. 并查集 (Union-Find) - 推荐2. 深度优先搜索 (DFS)3. 拓扑排序算法实现详解方法一&#xff1a;并查集 (Union-Find)方法二&#xff1a;深度优先搜索 (DFS)数学证明并查集算法正确性证明时间复…

Ⅹ—6.计算机二级综合题7---10套

目录 第7套 【填空题】 【修改题】 【设计题】 第8套 【填空题】 【修改题】 【设计题】 第9套 【填空题】 【修改题】 【设计题】 第10套 【填空题】 【修改题】 【设计题】 第7套 【填空题】 题目要求:给定程序中,函数fun的功能是:将形参s所指字符串中所…

【三桥君】大语言模型计算成本高,MoE如何有效降低成本?

​ 你好&#xff0c;我是 ✨三桥君✨ &#x1f4cc;本文介绍&#x1f4cc; >> 一、引言 在AI技术飞速发展的当下&#xff0c;大语言模型&#xff08;LLM&#xff09;的参数规模不断增长&#xff0c;但随之而来的计算成本问题也日益凸显。如何在保持高效推理能力的同时扩…

Python游戏开发利器:Pygame从入门到实战全解析

引言 Pygame是Python中最受欢迎的2D游戏开发库之一&#xff0c;基于SDL&#xff08;Simple DirectMedia Layer&#xff09;构建&#xff0c;支持图形渲染、音效处理、事件响应等核心功能。无论是开发简单的休闲游戏&#xff0c;还是复杂的交互式应用&#xff0c;Pygame都能提供…

行为型模式-协作与交互机制

行为型模式聚焦于对象间的行为交互&#xff0c;通过规范对象协作方式提升系统的灵活性与可扩展性。在分布式系统中&#xff0c;由于多节点异步通信、网络不可靠性及状态一致性挑战&#xff0c;行为型模式需针对分布式特性进行适应性设计。本文从观察者、策略、命令、责任链、状…

spring boot 整合 Spring Cloud、Kafka 和 MyBatis菜鸟教程

环境准备确保项目中已引入 Spring Boot、Spring Cloud、Kafka 和 MyBatis 的依赖。以下是一个典型的 Maven 依赖配置&#xff1a;<dependencies><!-- Spring Boot Starter --><dependency><groupId>org.springframework.boot</groupId><artif…

20 BTLO 蓝队靶场 Sticky Situation 解题记录

难度&#xff1a;5/10考察技能: Windows admin, Autopsy 使用场景&#xff1a;分析USB设备使用情况Autopsy使用注意&#xff1a;用管理员打开&#xff0c;在实际分析时注意先复制一个镜像文件&#xff0c;保存好原文件常用的Windows USB 取证的位置:Windows XP:Registry Key: U…

安装及配置Go语言开发环境与VSCode集成指南

安装Go语言开发 安装Go语言开发环境是第一步。访问Go官网&#xff0c;下载适合操作系统的安装包&#xff0c;如果进不去可以访问Go官方镜像站。 根据自己的系统选择对应的安装包&#xff0c;我这边是Windows系统就点击安装第一个即可。 点击下一步即可。 验证安装是否成功可以…