0%

原子操作:Java 并发安全的基石

在多线程环境中,对共享变量的操作若不加以控制,可能导致数据不一致(如i++这类看似原子的操作,实际包含 “读取 - 修改 - 写入” 三步)。Java 的java.util.concurrent.atomic包提供了一系列原子操作类,通过底层的 CAS(Compare-And-Swap)机制,确保操作的原子性,避免线程安全问题。

原子操作类的核心原理

原子操作类的本质是通过Unsafe 类实现的 CAS 操作,其核心思想是:乐观锁—— 假设操作不会冲突,若冲突则重试,直到成功。以最常用的AtomicInteger为例,其内部通过volatile修饰的value存储值,并借助 Unsafe 的 native 方法实现原子更新。

AtomicInteger 的核心源码解析

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
public class AtomicInteger extends Number implements java.io.Serializable {
private static final long serialVersionUID = 6214790243416807050L;

// 用于执行底层CAS操作的Unsafe实例
private static final Unsafe unsafe = Unsafe.getUnsafe();
// value字段在对象中的内存偏移量(用于CAS定位)
private static final long valueOffset;

static {
try {
// 获取value字段的内存偏移量
valueOffset = unsafe.objectFieldOffset(AtomicInteger.class.getDeclaredField("value"));
} catch (Exception ex) {
throw new Error(ex);
}
}

// 存储实际值,用volatile保证可见性
private volatile int value;

// 核心方法解析:

// 1. 获取当前值(volatile保证可见性)
public final int get() {
return value;
}

// 2. 原子更新:若当前值等于预期值,则更新为新值
public final boolean compareAndSet(int expect, int update) {
// 调用Unsafe的CAS方法,原子性地比较并替换
return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
}

// 3. 原子自增(i++的线程安全版)
public final int getAndIncrement() {
// 先获取当前值,再加1(底层通过循环CAS实现)
return unsafe.getAndAddInt(this, valueOffset, 1);
}

// 4. 原子自增(++i的线程安全版)
public final int incrementAndGet() {
// 先加1,再返回新值
return unsafe.getAndAddInt(this, valueOffset, 1) + 1;
}

// 其他方法:getAndDecrement()、decrementAndGet()、getAndAdd()等原理类似
}

CAS 操作的底层逻辑

unsafe.compareAndSwapInt(this, valueOffset, expect, update)的工作流程:

阅读全文 »

Servlet 3.0 异步处理:提升 Web 应用并发能力的关键机制

在传统的 Servlet 处理模型中,一个请求会占用容器线程直到响应完成,若遇到耗时操作(如数据库查询、远程接口调用),线程会被长期阻塞,导致线程资源耗尽,并发能力下降。Servlet 3.0 引入异步处理机制,允许线程在启动耗时操作后立即返回容器,待操作完成后再通过异步上下文处理响应,显著提升了资源利用率和并发吞吐量。本文将详细解析异步处理的原理、核心 API 及实践方式。

异步处理的核心原理

传统同步处理的问题

在同步处理中,容器为每个请求分配一个线程,线程会全程参与请求处理(接收请求→业务处理→生成响应)。若业务处理包含耗时操作(如等待数据库返回),线程会处于阻塞状态,无法处理其他请求,导致:

  • 线程资源浪费(阻塞时不做有效工作);
  • 高并发下线程池耗尽,新请求被拒绝。

异步处理的改进

异步处理允许线程在启动耗时操作后立即释放,返回容器处理其他请求;待耗时操作完成后,再通过新的线程(或回调)处理响应。流程如下:

  1. 容器线程接收请求,启动异步处理,获取 AsyncContext 对象;
  2. 容器线程将耗时操作(如数据库查询)委托给后台线程,自身立即返回容器;
  3. 后台线程完成耗时操作后,通过 AsyncContext 生成响应,结束异步处理。

核心价值:减少线程阻塞时间,提高线程利用率,支持更高的并发请求。

异步处理的核心 API

Servlet 3.0 通过 ServletRequestAsyncContext 接口提供异步处理能力,核心类和方法如下:

ServletRequest 中的异步方法

方法 作用描述
startAsync() 启动异步处理,返回 AsyncContext 对象(使用原始请求和响应)。
startAsync(ServletRequest, ServletResponse) 启动异步处理,使用包装后的请求 / 响应对象(如 ServletRequestWrapper)。
isAsyncStarted() 判断当前请求是否处于异步模式。
isAsyncSupported() 判断当前 Servlet 是否支持异步处理(需配置 asyncSupported=true)。
getAsyncContext() 获取当前请求的 AsyncContext 对象(仅在异步模式下有效)。

AsyncContext 接口(异步上下文)

AsyncContext 是异步处理的核心对象,封装了请求 / 响应的上下文,提供控制异步流程的方法:

阅读全文 »

ConcurrentMap接口及其实现类深度解析

ConcurrentMap 接口是 Java 并发包(JUC)中定义的线程安全 Map 接口,继承自 Map 接口,提供了原子性操作方法(如 putIfAbsentremovereplace 等)。本文将深入分析其两大核心实现类:ConcurrentHashMapConcurrentSkipListMap,并探讨它们的设计原理、适用场景及性能差异。

ConcurrentHashMap:高并发哈希表的进化之路

JDK7 与 JDK8 的架构差异

JDK7 分段锁架构
  • 分段锁(Segment):将整个 Map 分为 16 个 Segment(类似小型 HashMap),每个 Segment 独立加锁,不同 Segment 可并发读写,提高并发度;
  • 两次哈希:第一次定位 Segment,第二次定位元素所在链表;
  • 缺点:锁粒度较大,并发度受限于 Segment 数量,且结构复杂。
JDK8 数组 + 链表 + 红黑树
  • 取消分段锁:采用 Node 数组 + 链表 / 红黑树结构,直接对数组节点加锁(锁粒度更小);
  • CAS + synchronized:使用 CAS 处理无锁场景(如初始化、空槽插入),synchronized 处理竞争场景(如链表插入、红黑树操作);
  • 优化点
    • 链表长度超过 8 时自动转换为红黑树(O (n) → O (logn));
    • 扩容时多线程协助迁移数据,提升效率。

JDK8 核心源码解析

重要变量与机制
阅读全文 »

Netty EventLoop 组件深度解析:线程模型的核心执行者

EventLoop 是 Netty 线程模型的核心组件,负责处理 Channel 的 IO 事件和任务调度,其设计直接影响 Netty 的并发性能。本文将从 EventLoopGroup 与 EventLoop 的关系、内部实现机制到任务调度逻辑,全面解析这一组件的工作原理。

EventLoop 与 EventLoopGroup 的关系

核心定义

  • EventLoop:单线程执行器,负责处理一个或多个 Channel 的 IO 事件(如连接、读写),并调度任务(普通任务、定时任务)。
  • EventLoopGroup:EventLoop 的容器(线程池),提供线程管理和负载均衡功能,通过 next() 方法分配 EventLoop 处理 Channel。

NioEventLoop

类比理解
EventLoopGroup 相当于 “线程池”,EventLoop 相当于 “线程”,每个 EventLoop 绑定一个线程,负责处理分配给它的 Channel。

实例化与线程数配置

Netty 中最常用的实现是 NioEventLoopGroup(基于 Java NIO 的 Selector),其线程数配置规则:

  • 默认线程数为 CPU 核心数 × 2(充分利用多核资源)。
  • 可手动指定线程数(如 new NioEventLoopGroup(4) 表示 4 个线程)。
1
2
3
4
5
6
// 服务端:bossGroup 处理连接,workerGroup 处理 IO
EventLoopGroup bossGroup = new NioEventLoopGroup(1); // 通常 1 个线程足够
EventLoopGroup workerGroup = new NioEventLoopGroup(); // 默认 CPU×2 线程

// 客户端:单一线程组处理所有操作
EventLoopGroup group = new NioEventLoopGroup();
阅读全文 »

Netty Bootstrap 组件深度解析:客户端与服务端启动机制

Bootstrap 组件是 Netty 应用的 “启动器”,负责串联 EventLoopGroup、Channel、ChannelHandler 等核心组件,完成网络服务的初始化与配置。客户端的 Bootstrap 和服务端的 ServerBootstrap 虽功能相似,但在设计上针对不同场景做了专门优化。本文将详细解析两者的核心方法、配置逻辑及使用差异。

Bootstrap 与 ServerBootstrap 的核心定位

共同目标

  • 封装网络服务的启动流程,简化配置(如线程组、通道类型、处理器等)。
  • 通过链式调用(Fluent API)提供直观的配置方式。
  • 支持异步启动(返回 ChannelFuture),避免阻塞主线程。

核心差异

维度 Bootstrap(客户端) ServerBootstrap(服务端)
核心功能 连接远程服务器 绑定本地端口,监听客户端连接
线程组数量 1 个 EventLoopGroup(处理连接与 IO) 2 个 EventLoopGroup(parent 处理连接,child 处理 IO)
通道类型 客户端通道(如 NioSocketChannel 服务端通道(如 NioServerSocketChannel
关键操作 connect()(连接远程地址) bind()(绑定本地端口)

核心方法详解与配置逻辑

线程组配置(group()

线程组是 Netty 处理 IO 事件的核心资源,group() 方法用于绑定线程组。

客户端(Bootstrap

阅读全文 »