Java高质量编程【一、概述】
在Java生态中,构建高性能(High Performance)、高可用(High Availability)、高扩展性(High Scalability/Extensibility)的系统是架构师和高级开发者的核心目标。这三者往往相互制约(例如过度追求性能可能牺牲可维护性),因此需要权衡与最佳实践。
以下是针对这三个维度的详细原则、方法及正反代码示例。
一、高性能 (High Performance)
核心目标:降低延迟(Latency),提高吞吐量(Throughput),减少资源(CPU/内存)消耗。
1. 关键原则与方法
- 减少对象创建与GC压力:避免在循环中创建临时对象,使用对象池,优先使用基本类型。
- 并发编程优化:
- 优先使用无锁数据结构(如
LongAddervsAtomicLong)。 - 合理使用线程池,避免频繁创建销毁线程。
- 利用
CompletableFuture或虚拟线程(Java 21+)进行异步编排。
- 优先使用无锁数据结构(如
- I/O 模型:从阻塞 I/O (BIO) 转向非阻塞 I/O (NIO),使用 Netty 或 Project Loom。
- 缓存策略:多级缓存(本地 Caffeine + 分布式 Redis),注意缓存穿透/击穿/雪崩。
- JVM 调优:选择合适的 GC 收集器(G1, ZGC),调整堆大小。
2. 正反示例:高并发计数器
❌ 反面示例:低性能(锁竞争严重)
使用 synchronized 或 AtomicLong 在极高并发下会导致严重的 CAS 自旋失败或线程阻塞。
public class LowPerformanceCounter {
// 在高并发下,AtomicLong 的 CAS 操作会导致大量 CPU 空转(自旋)
private final AtomicLong count = new AtomicLong(0);
public void increment() {
// 每次调用都涉及一次 CAS 操作,竞争激烈时性能急剧下降
count.incrementAndGet();
}
public long getCount() {
return count.get();
}
}
✅ 正面示例:高性能(分段思想/LongAdder)
使用 LongAdder,它内部采用分段累加(类似 ConcurrentHashMap 的分段锁思想),仅在取值时合并,极大降低了竞争。
import java.util.concurrent.atomic.LongAdder;
public class HighPerformanceCounter {
// LongAdder 在多线程高并发场景下性能远优于 AtomicLong
private final LongAdder count = new LongAdder();
public void increment() {
// 不同线程更新不同的 Cell,减少冲突
count.increment();
}
public long getCount() {
// 仅在读取时进行汇总,读取频率通常远低于写入
return count.sum();
}
}
3. 正反示例:字符串拼接
❌ 反面示例
在循环中使用 + 拼接字符串,导致大量临时 StringBuilder 和 String 对象创建,触发频繁 GC。
public String buildReportBad(List<String> items) {
String result = "";
for (String item : items) {
// 每次循环都创建新的 StringBuilder 和 String 对象
result += item + ",";
}
return result;
}
✅ 正面示例
预分配容量的 StringBuilder。
public String buildReportGood(List<String> items) {
// 预估大小,减少扩容拷贝
StringBuilder sb = new StringBuilder(items.size() * 10);
for (String item : items) {
sb.append(item).append(",");
}
return sb.toString();
}
二、高可用 (High Availability)
核心目标:系统在面对故障(硬件、网络、代码Bug、流量洪峰)时仍能持续提供服务,最小化停机时间(MTTR)。
1. 关键原则与方法
- 故障隔离(Bulkheading):一个模块的故障不应拖垮整个系统(如线程池隔离、信号量隔离)。
- 熔断与降级(Circuit Breaker & Fallback):当下游服务不可用时,快速失败并返回默认值,防止级联故障。
- 超时控制(Timeouts):所有远程调用必须设置超时,避免线程被无限期占用。
- 重试机制(Retry with Backoff):对瞬时故障进行指数退避重试,但要避免重试风暴。
- 幂等性(Idempotency):确保重复请求不会产生副作用。
2. 正反示例:远程服务调用
❌ 反面示例:无保护调用(雪崩风险)
没有超时、没有熔断,一旦下游服务卡顿,当前服务线程池迅速耗尽,导致整个应用不可用。
public class VulnerableService {
public UserData getUserData(String userId) {
// 1. 没有超时设置:如果 rpcClient 卡住,线程将永久阻塞
// 2. 没有熔断:下游挂了,这里会一直尝试,拖垮线程池
// 3. 没有降级:直接抛出异常,用户看到 500 错误
return rpcClient.callRemote(userId);
}
}
✅ 正面示例:使用 Resilience4j 进行防护
引入熔断器、超时控制和降级逻辑。
import io.github.resilience4j.circuitbreaker.CircuitBreaker;
import io.github.resilience4j.timelimiter.TimeLimiter;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService;
public class ResilientService {
private final CircuitBreaker circuitBreaker;
private final TimeLimiter timeLimiter;
private final ExecutorService executorService;
public UserData getUserDataSafe(String userId) {
try {
// 组合使用:超时控制 + 熔断保护
CompletableFuture<UserData> future = timeLimiter.executeFutureSupplier(
() -> CompletableFuture.supplyAsync(() ->
circuitBreaker.executeSupplier(() -> rpcClient.callRemote(userId))
, executorService),
java.time.Duration.ofSeconds(2) // 2秒超时
);
return future.join();
} catch (Exception e) {
// 降级逻辑:返回缓存数据或默认值,保证系统不崩
return getFallbackUserData(userId);
}
}
private UserData getFallbackUserData(String userId) {
// 从本地缓存获取或返回空对象
return new UserData(userId, "DefaultName", true);
}
}
三、高扩展性 (High Scalability & Extensibility)
这里包含两个层面:
- Scalability (可伸缩性):通过增加机器轻松应对流量增长(水平扩展)。
- Extensibility (可扩展性/易维护性):代码结构易于添加新功能,符合开闭原则(OCP)。
1. 关键原则与方法
- 无状态设计 (Stateless):服务节点不保存会话状态,便于负载均衡和横向扩容。
- 领域驱动设计 (DDD) 与 分层架构:清晰的边界,业务逻辑与技术细节分离。
- 开闭原则 (OCP):对扩展开放,对修改关闭。使用策略模式、模板方法模式、SPI 机制。
- 事件驱动 (Event-Driven):通过消息队列解耦模块,实现异步扩展。
- 依赖注入 (DI):利用 Spring 等框架管理生命周期和解耦。
2. 正反示例:支付渠道扩展
❌ 反面示例:硬编码/违反开闭原则
每新增一种支付方式(如 PayPal),都需要修改核心代码,增加 if-else,容易引入 Bug,且难以测试。
public class PaymentProcessor {
public void pay(String type, double amount) {
if ("ALIPAY".equals(type)) {
// 支付宝逻辑
connectToAlipay(amount);
} else if ("WECHAT".equals(type)) {
// 微信逻辑
connectToWechat(amount);
} else if ("CREDIT_CARD".equals(type)) {
// 信用卡逻辑
connectToBank(amount);
} else {
throw new IllegalArgumentException("Unsupported type");
}
// 记录日志、发送通知等逻辑混杂在一起
}
// 随着支付方式增加,这个类会变得极其庞大且难以维护
}
✅ 正面示例:策略模式 + 工厂/Spring 注入
定义统一接口,每种支付方式实现该接口。新增支付方式只需新增一个类,无需修改原有代码。
// 1. 定义策略接口
public interface PaymentStrategy {
void pay(double amount);
boolean supports(String type);
}
// 2. 具体实现
@Component
public class AlipayStrategy implements PaymentStrategy {
public boolean supports(String type) { return "ALIPAY".equals(type); }
public void pay(double amount) { /* 支付宝具体逻辑 */ }
}
@Component
public class WechatStrategy implements PaymentStrategy {
public boolean supports(String type) { return "WECHAT".equals(type); }
public void pay(double amount) { /* 微信具体逻辑 */ }
}
// 3. 上下文/工厂类 (高扩展性核心)
@Service
public class PaymentService {
// 利用 Spring 自动注入所有实现了 PaymentStrategy 的 Bean
private final List<PaymentStrategy> strategies;
public PaymentService(List<PaymentStrategy> strategies) {
this.strategies = strategies;
}
public void processPayment(String type, double amount) {
// 查找对应的策略
PaymentStrategy strategy = strategies.stream()
.filter(s -> s.supports(type))
.findFirst()
.orElseThrow(() -> new IllegalArgumentException("Unknown payment type: " + type));
// 执行支付
strategy.pay(amount);
// 横切关注点(日志、监控)可以在此统一处理或通过 AOP 处理
}
}
优势:如果要加 "Bitcoin" 支付,只需新建 BitcoinStrategy 类并加上 @Component,主流程代码一行都不用改。
3. 正反示例:水平扩展性 (Stateless)
❌ 反面示例:有状态服务
将用户 Session 存储在本地内存中,导致无法通过 Nginx 进行简单的轮询负载均衡(需要配置 Sticky Session),且单机故障会导致用户登录态丢失。
@Service
public class CartService {
// 致命缺陷:状态绑定在单机内存中
private final Map<String, List<Item>> localCartCache = new ConcurrentHashMap<>();
public void addItem(String userId, Item item) {
localCartCache.computeIfAbsent(userId, k -> new ArrayList<>()).add(item);
}
}
✅ 正面示例:无状态 + 外部存储
服务本身无状态,状态下沉到 Redis 或数据库。任何一台服务器都可以处理任何用户的请求。
@Service
public class StatelessCartService {
@Autowired
private RedisTemplate<String, Object> redisTemplate;
public void addItem(String userId, Item item) {
// 状态存储在共享的 Redis 中
String key = "cart:" + userId;
redisTemplate.opsForList().rightPush(key, item);
// 服务实例可以随时增加或减少,不影响业务
}
}
四、综合总结与检查清单
要编写同时具备“三高”特性的 Java 代码,建议在 Code Review 时对照以下清单:
| 维度 | 检查点 (Checklist) | 推荐工具/库 |
|---|---|---|
| 高性能 | [ ] 是否避免了循环内的对象创建?[ ] 集合是否预设了初始容量?[ ] 高并发计数是否使用了 LongAdder?[ ] 数据库查询是否有 N+1 问题?[ ] 是否利用了异步/非阻塞 I/O? |
JMH (基准测试), Async Profiler, Arthas |
| 高可用 | [ ] 所有 RPC/DB 调用是否有 Timeout?[ ] 是否有熔断和降级策略?[ ] 异常是否被合理捕获并记录,而不是吞掉或裸抛?[ ] 关键路径是否有重试机制(带退避)?[ ] 是否有健康检查接口 (/health)? |
Resilience4j, Sentinel, Hystrix (旧) |
| 高扩展 | [ ] 是否符合单一职责原则 (SRP)?[ ] 新增功能是否需要修改现有核心类 (OCP)?[ ] 服务是否无状态 (Stateless)?[ ] 模块间是否通过接口/事件解耦?[ ] 配置是否与代码分离? | Spring Boot, DDD, Kafka/RocketMQ |
五、进阶建议
-
拥抱虚拟线程 (Project Loom, Java 21+):
- 传统线程模型下,高并发需要复杂的异步回调(Reactive),代码难读。
- Java 21 的虚拟线程允许用同步的代码风格写出高并发的程序,极大地平衡了性能与可维护性。
// Java 21+ 示例:成千上万个虚拟线程成本极低 try (var executor = Executors.newVirtualThreadPerTaskExecutor()) { IntStream.range(0, 10_000).forEach(i -> { executor.submit(() -> { Thread.sleep(Duration.ofSeconds(1)); // 阻塞不会浪费平台线程 return i; }); }); } -
可观测性 (Observability):
- 没有监控的高性能代码是盲目的。必须集成 Metrics (Micrometer), Tracing (OpenTelemetry), Logging (Structured Logging)。
- 只有知道系统慢在哪里,才能优化性能;只有知道故障发生在哪里,才能保证可用性。
-
测试驱动:
- 单元测试保证逻辑正确(扩展性的基础)。
- 压力测试 (JMeter/Gatling) 验证性能瓶颈。
- 混沌工程 (Chaos Mesh) 验证高可用性(随机杀 Pod、模拟网络延迟)。
通过遵循上述原则和模式,你可以构建出既能在双11流量洪峰下稳如泰山,又能快速响应业务需求变化的健壮 Java 系统。
本文来自博客园,作者:蓝迷梦,转载请注明原文链接:https://www.cnblogs.com/hewei-blogs/articles/19714139

浙公网安备 33010602011771号