0%

LongAdder:高并发下的高效原子累加器

在高并发场景中,AtomicLong虽然能保证原子性,但大量线程竞争同一个变量时,CAS 操作的失败重试会导致CPU 自旋开销激增。JDK 8 引入的LongAdder通过分散热点的设计,显著提升了高并发下的累加性能,成为计数器场景的首选工具。

LongAdder 的核心设计思想

LongAdder的核心是分而治之:将一个全局计数器拆分为多个局部计数器(Cell数组),线程优先操作自己对应的局部计数器,最后通过累加局部计数器的值得到全局结果。

  • 低并发场景:直接操作base变量(类似AtomicLong);
  • 高并发场景:线程分散到不同的Cell中进行累加,减少竞争。

LongAdder 的内部结构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public class LongAdder extends Striped64 implements Serializable {
// 存储局部计数器的数组(延迟初始化)
transient volatile Cell[] cells;
// 基础值(低并发时直接使用,或作为备用)
transient volatile long base;
// 自旋锁标识(0:无锁;1:被占用,用于初始化/扩容cells或创建Cell)
transient volatile int cellsBusy;

// 局部计数器Cell类(使用@Contended避免伪共享)
@sun.misc.Contended
static final class Cell {
volatile long value; // 存储局部累加值
Cell(long x) { value = x; }
// CAS操作更新value
final boolean cas(long cmp, long val) {
return UNSAFE.compareAndSwapLong(this, valueOffset, cmp, val);
}
// 省略Unsafe相关代码...
}
}

关键细节:

  • @Contended 注解:避免Cell数组元素因 CPU 缓存行共享导致的伪共享问题(提升缓存利用率);
  • volatile 修饰cellsbaseCell.value均用volatile保证可见性;
  • cellsBusy:通过 CAS 控制cells数组的初始化、扩容和Cell创建的原子性。

核心方法解析:add (long x)

add方法是LongAdder的核心,逻辑是优先操作 Cell,失败则退化为操作 base

阅读全文 »

Spring Cloud 容器机制:父子容器的协同与隔离

在 Spring Cloud 中,为了满足微服务架构中配置隔离、早期初始化等需求,并非只存在一个 Spring 容器,而是通过多次创建容器形成父子容器层级结构。这些容器各司其职,协同完成微服务的初始化、配置加载和组件管理。

Spring Cloud 容器的层级关系

Spring Cloud 的容器体系由三个核心上下文(容器)构成,形成 “祖先→父→子” 的层级:

  • Bootstrap 上下文:最顶层的祖先容器,负责早期初始化和核心配置加载。
  • Spring Boot 上下文:中间层的父容器,即我们日常开发中最常接触的应用容器。
  • 微服务配置上下文:底层的子容器,为 Feign、Ribbon 等组件提供配置隔离。

它们的关系如下:

1
2
3
4
5
Bootstrap 上下文(祖先)
↓(作为父容器)
Spring Boot 上下文(主应用容器)
↓(作为父容器)
微服务配置上下文(如 FeignContext、LoadBalancerClientFactory 等,隔离组件配置)

核心容器详解

Bootstrap 上下文:祖先容器,早期初始化的基石

作用
阅读全文 »

MySQL 中 COUNT 函数的用法与区别详解

COUNT() 是 MySQL 中用于统计记录行数的聚合函数,但其参数不同(如 COUNT(*)COUNT(1)COUNT(列名))时,行为和性能存在差异。理解这些差异有助于写出更高效、准确的统计语句。

COUNT 函数的三种常见用法

1. COUNT(*)

  • 作用:统计所有记录的行数,包括 NULL 值(无论字段是否为 NULL,只要行存在就计数)。
  • 特点:
    • 不忽略任何行,包括包含 NULL 的行。
    • InnoDB 引擎对 COUNT(*) 进行了优化,会自动选择最小的索引(非主键索引优先)来加速统计,性能较好。

示例

1
2
-- 统计表中所有记录(包括字段为NULL的行)
SELECT COUNT(*) FROM doc; -- 结果:188(包含所有行)

2. COUNT(1)

  • 作用:统计所有记录的行数,原理与 COUNT(*) 类似(用常量 1 代替字段,只要行存在就计数)。
  • 特点:
    • 同样包含 NULL 值的行(因为 1 是常量,永远非 NULL)。
    • 在 InnoDB 中,COUNT(1)COUNT(*) 性能几乎一致,优化方式相同(优先使用最小索引)。

示例

1
2
-- 结果与 COUNT(*) 相同
SELECT COUNT(1) FROM doc; -- 结果:188(与 COUNT(*) 一致)

3. COUNT(列名)

  • 作用:统计指定列中非 NULL 值的行数(自动忽略 NULL 值)。
  • 特点:
    • 仅计数该列值不为 NULL 的行(若列允许 NULL,结果可能小于总行数)。
    • 若该列上有索引,会使用索引加速统计;否则会全表扫描,性能可能较差。
阅读全文 »

Spring 文件上传机制详解:从请求解析到实战配置

文件上传是 Web 开发中的常见需求,Spring 提供了灵活且强大的文件上传支持,核心通过 MultipartResolver 接口解析 multipart/form-data 格式的请求。从 “请求格式→解析原理→两种实现类对比→实战配置→常见问题” 五个维度,全面解析 Spring 文件上传的底层机制与最佳实践。

文件上传的基础:multipart/form-data 格式

在讨论 Spring 的实现前,需先明确文件上传的 HTTP 协议基础:客户端必须使用 multipart/form-data 格式提交请求,这是由 RFC 1867 定义的专门用于文件上传的 MIME 类型。

格式特点

  • 数据结构:请求体被分割为多个 “部分(Part)”,每个部分对应一个表单字段(普通字段或文件字段);
  • 分隔符:各部分通过一个唯一的 “边界字符串(Boundary)” 分隔,边界字符串在请求头 Content-Type 中指定(如 Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryABC123);
  • 支持内容:可同时传输文件(二进制数据)和普通键值对(文本数据),无需 URL 编码。

示例请求(简化版)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
POST /upload HTTP/1.1
Host: localhost:8080
Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryABC123
Content-Length: 12345

----WebKitFormBoundaryABC123
Content-Disposition: form-data; name="username"

张三
----WebKitFormBoundaryABC123
Content-Disposition: form-data; name="avatar"; filename="photo.jpg"
Content-Type: image/jpeg

[二进制图片数据]
----WebKitFormBoundaryABC123--

Spring 文件上传核心:MultipartResolver 接口

Spring 通过 MultipartResolver 接口抽象文件上传的解析逻辑,定义了判断请求是否为 multipart 类型以及解析请求的核心方法:

阅读全文 »

Docker 简介:轻量级容器技术的核心解析

Docker 作为近年来备受关注的容器化技术,彻底改变了软件的开发、部署和运维方式。它通过封装、隔离和标准化,解决了 “在我机器上能运行,在你机器上不能运行” 的经典问题,尤其适合微服务架构下的应用交付。

Docker 的核心本质

Docker 是一种轻量级容器技术,与传统虚拟机技术相比,它不依赖硬件虚拟化,而是直接运行在宿主操作系统内核之上,通过 Linux 内核的三大技术实现核心功能:

  • Namespace(命名空间):实现资源隔离(如进程、网络、文件系统等),让容器拥有独立的 “系统环境”;
  • Cgroup(控制组):限制容器对 CPU、内存、磁盘 IO 等资源的使用,防止单个容器耗尽宿主机资源;
  • 写时复制(Copy-on-Write):优化镜像存储和容器启动速度,多个容器可共享基础镜像,仅在修改时复制差异内容。

Docker 的核心组成

docker

Docker 引擎(Docker Engine)

  • 运行在宿主机上的后台进程(dockerd),负责管理镜像、容器、网络等核心资源;
  • 提供 API 接口,供客户端(如命令行工具)调用。

Docker 客户端(Docker Client)

  • 与用户交互的入口,支持命令行(docker命令)和 REST API 两种方式;
  • 用户通过客户端向 Docker 引擎发送指令(如docker rundocker pull)。

Docker 镜像(Docker Images)

阅读全文 »