Skip to main content

综合121-140

121. 到底什么是 TCP 连接?

展开 简单 VIP 网络

TCP连接定义: TCP连接是一个抽象概念,它是通信双方维护的一组状态信息,用于可靠地传输数据。

连接的本质:

  • 不是物理连接:网络层面没有专用的物理线路
  • 状态维护:双方各自维护连接状态信息
  • 虚拟电路:在不可靠的网络上建立可靠的通信通道

连接状态信息包括:

  • 序列号(发送、接收)
  • 窗口大小
  • 最大段长度(MSS)
  • 往返时间(RTT)
  • 拥塞控制参数

连接标识(四元组):

(源IP, 源端口, 目标IP, 目标端口)

生命周期:

  1. 建立:三次握手创建连接
  2. 数据传输:可靠的双向数据传输
  3. 关闭:四次挥手终止连接

特点:

  • 面向连接的协议
  • 全双工通信
  • 字节流服务
  • 可靠性保证

122. 如何处理重复消息?

展开 中等 VIP 消息队列 后端

重复消息产生原因:

  • 网络重传
  • 生产者重试
  • 消费者重复拉取
  • 系统故障恢复

解决方案:

1. 幂等性设计

// 使用唯一ID确保操作幂等
@Service
public class OrderService {
public void processOrder(String orderId, OrderInfo order) {
if (orderExists(orderId)) {
return; // 已处理过,直接返回
}
// 处理订单逻辑
saveOrder(orderId, order);
}
}

2. 去重表

  • 消息ID作为主键
  • 处理前先检查是否存在
  • 利用数据库唯一约束

3. Redis分布式锁

public boolean processMessage(String messageId) {
String lockKey = "msg_lock:" + messageId;
if (redisTemplate.setIfAbsent(lockKey, "1", Duration.ofMinutes(5))) {
try {
// 处理消息逻辑
return true;
} finally {
redisTemplate.delete(lockKey);
}
}
return false; // 重复消息
}

4. 状态机模式

  • 设计合理的状态转换
  • 确保状态变更的幂等性

最佳实践:

  • 业务层面设计幂等接口
  • 使用全局唯一ID
  • 合理设置去重窗口期
  • 监控重复消息比例

123. Java 中什么情况会导致死锁?如何避免?

展开 中等 Java并发 Java

死锁的四个必要条件:

  1. 互斥条件:资源不能被多个线程同时使用
  2. 持有等待:线程持有资源的同时等待其他资源
  3. 不可剥夺:资源只能被持有者主动释放
  4. 循环等待:形成资源等待的环路

常见死锁场景:

1. 锁顺序死锁

// 错误示例:可能导致死锁
public class DeadlockExample {
private Object lock1 = new Object();
private Object lock2 = new Object();

public void method1() {
synchronized(lock1) {
synchronized(lock2) {
// 业务逻辑
}
}
}

public void method2() {
synchronized(lock2) {
synchronized(lock1) { // 与method1顺序相反
// 业务逻辑
}
}
}
}

2. 动态锁顺序死锁

// 根据对象hash值排序避免死锁
public void transfer(Account from, Account to, int amount) {
int fromHash = System.identityHashCode(from);
int toHash = System.identityHashCode(to);

if (fromHash < toHash) {
synchronized(from) {
synchronized(to) {
transfer(from, to, amount);
}
}
} else {
synchronized(to) {
synchronized(from) {
transfer(from, to, amount);
}
}
}
}

避免死锁的方法:

1. 固定锁顺序

  • 所有线程按相同顺序获取锁

2. 超时机制

if (lock1.tryLock(1000, TimeUnit.MILLISECONDS)) {
try {
if (lock2.tryLock(1000, TimeUnit.MILLISECONDS)) {
try {
// 业务逻辑
} finally {
lock2.unlock();
}
}
} finally {
lock1.unlock();
}
}

3. 使用并发工具类

  • CountDownLatch
  • Semaphore
  • CyclicBarrier

4. 减少锁的使用

  • 使用无锁数据结构
  • 减小锁的粒度
  • 使用读写锁

124. 如何保证消息的有序性?

展开 中等 VIP 消息队列 后端

有序性级别:

1. 全局有序

  • 整个系统中所有消息严格有序
  • 性能代价大,一般不推荐

2. 分区有序

  • 同一分区内消息有序
  • 平衡了性能和有序性

3. 业务有序

  • 相关联的消息有序
  • 最常用的方案

实现方案:

1. 单队列方案

// 生产者发送到同一队列
producer.send("order-queue", message);

// 消费者单线程消费
@RabbitListener(queues = "order-queue", concurrency = "1")
public void handleMessage(OrderMessage message) {
// 处理消息
}

2. 分区路由

// 根据业务key路由到固定分区
public class OrderProducer {
public void sendOrder(OrderMessage message) {
String partitionKey = message.getUserId();
producer.send("order-topic", partitionKey, message);
}
}

3. 消息编号

@Component
public class OrderedMessageConsumer {
private Map<String, Long> lastSequenceMap = new ConcurrentHashMap<>();

public void processMessage(String userId, Long sequence, Object message) {
Long lastSequence = lastSequenceMap.get(userId);
if (lastSequence == null || sequence == lastSequence + 1) {
// 处理消息
lastSequenceMap.put(userId, sequence);
} else {
// 乱序处理:缓存或重试
handleOutOfOrder(userId, sequence, message);
}
}
}

RocketMQ有序消息示例:

// 生产者
producer.send(message, new MessageQueueSelector() {
@Override
public MessageQueue select(List<MessageQueue> mqs, Message msg, Object arg) {
String orderId = (String) arg;
int index = orderId.hashCode() % mqs.size();
return mqs.get(Math.abs(index));
}
}, orderId);

// 消费者
consumer.registerMessageListener(new MessageListenerOrderly() {
@Override
public ConsumeOrderlyStatus consumeMessage(
List<MessageExt> messages,
ConsumeOrderlyContext context) {
// 按顺序处理消息
return ConsumeOrderlyStatus.SUCCESS;
}
});

注意事项:

  • 有序性和性能是矛盾的
  • 合理选择有序性级别
  • 考虑消费者故障的影响
  • 避免消息积压

125. 说一下 Netty 的应用场景?

展开 中等 VIP Netty 后端

Netty简介: Netty是基于NIO的客户端-服务器框架,提供异步的事件驱动的网络应用程序框架。

核心特性:

  • 高性能的NIO实现
  • 事件驱动架构
  • 丰富的协议支持
  • 内存管理优化
  • 线程模型优秀

主要应用场景:

1. RPC框架

// Dubbo底层使用Netty
// gRPC的Java实现也使用Netty
public class RPCServer {
private EventLoopGroup bossGroup = new NioEventLoopGroup(1);
private EventLoopGroup workerGroup = new NioEventLoopGroup();

public void start() throws Exception {
ServerBootstrap bootstrap = new ServerBootstrap();
bootstrap.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class)
.childHandler(new RPCServerInitializer());

ChannelFuture future = bootstrap.bind(8080).sync();
}
}

2. 游戏服务器

  • 实时性要求高
  • 大量并发连接
  • 自定义协议支持

3. 即时通讯

// WebSocket聊天服务器
public class ChatServerInitializer extends ChannelInitializer<SocketChannel> {
@Override
protected void initChannel(SocketChannel ch) {
ChannelPipeline pipeline = ch.pipeline();
pipeline.addLast(new HttpServerCodec());
pipeline.addLast(new HttpObjectAggregator(64 * 1024));
pipeline.addLast(new WebSocketServerProtocolHandler("/chat"));
pipeline.addLast(new ChatServerHandler());
}
}

4. HTTP服务器

  • 高并发Web服务
  • API网关
  • 代理服务器

5. 消息队列

  • RocketMQ使用Netty作为通信层
  • Kafka的客户端使用Netty

6. 分布式系统通信

  • 微服务间通信
  • 集群节点通信
  • 分布式存储系统

7. 物联网(IoT)

  • MQTT协议实现
  • 设备数据收集
  • 边缘计算

8. 大数据传输

  • Hadoop的数据传输
  • 流式数据处理
  • 文件传输服务

选择Netty的原因:

  • 比原生NIO更易用
  • 高性能、低延迟
  • 内存管理优秀
  • 丰富的编解码器
  • 稳定的API设计

知名项目使用Netty:

  • Dubbo、Elasticsearch
  • Cassandra、HBase
  • Spark、Flink

126. 什么是 Spring IOC?

展开 中等 VIP 后端 Spring

IOC定义: IOC(Inversion of Control)控制反转,是一种设计原则,将对象创建和依赖管理的控制权从应用程序转移到框架。

核心概念:

1. 依赖注入(DI)

  • 构造器注入
  • Setter注入
  • 字段注入

2. IOC容器

  • BeanFactory:基础容器
  • ApplicationContext:高级容器

传统方式 vs IOC方式:

传统方式:

public class UserService {
private UserDao userDao = new UserDaoImpl(); // 硬编码依赖

public User getUser(Long id) {
return userDao.findById(id);
}
}

IOC方式:

@Service
public class UserService {
@Autowired
private UserDao userDao; // 由Spring注入

public User getUser(Long id) {
return userDao.findById(id);
}
}

IOC的优势:

1. 降低耦合度

  • 对象不直接创建依赖
  • 易于测试和维护

2. 提高可扩展性

  • 通过配置切换实现
  • 支持多种注入方式

3. 统一管理

  • 对象生命周期管理
  • 单例、原型等作用域

Bean的生命周期:

1. 实例化 → 2. 属性注入 → 3. Aware接口回调 → 
4. BeanPostProcessor前置处理 → 5. 初始化方法 →
6. BeanPostProcessor后置处理 → 7. 使用 → 8. 销毁

配置方式:

1. XML配置

<bean id="userService" class="com.example.UserService">
<property name="userDao" ref="userDao"/>
</bean>

2. 注解配置

@Component
public class UserService {
@Autowired
private UserDao userDao;
}

3. Java配置

@Configuration
public class AppConfig {
@Bean
public UserService userService() {
return new UserService(userDao());
}
}

容器类型:

  • BeanFactory:延迟初始化,轻量级
  • ApplicationContext:立即初始化,功能丰富

127. 你了解 Java 线程池的原理吗?

展开 中等 Java并发 Java

线程池核心参数:

public ThreadPoolExecutor(
int corePoolSize, // 核心线程数
int maximumPoolSize, // 最大线程数
long keepAliveTime, // 空闲线程存活时间
TimeUnit unit, // 时间单位
BlockingQueue<Runnable> workQueue, // 工作队列
ThreadFactory threadFactory, // 线程工厂
RejectedExecutionHandler handler // 拒绝策略
)

工作流程:

1. 当前线程数 < corePoolSize → 创建新线程执行任务
2. 核心线程已满 → 任务加入队列等待
3. 队列已满 + 当前线程数 < maximumPoolSize → 创建临时线程
4. 线程数达到maximumPoolSize → 执行拒绝策略

线程池状态:

// 线程池状态转换
RUNNINGSHUTDOWNSTOPTIDYINGTERMINATED

核心源码分析:

public void execute(Runnable command) {
int c = ctl.get();
// 1. 工作线程数小于核心线程数
if (workerCountOf(c) < corePoolSize) {
if (addWorker(command, true))
return;
c = ctl.get();
}
// 2. 加入工作队列
if (isRunning(c) && workQueue.offer(command)) {
int recheck = ctl.get();
if (!isRunning(recheck) && remove(command))
reject(command);
else if (workerCountOf(recheck) == 0)
addWorker(null, false);
}
// 3. 创建临时线程或拒绝
else if (!addWorker(command, false))
reject(command);
}

常用线程池类型:

1. FixedThreadPool

public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}

2. CachedThreadPool

public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}

3. SingleThreadExecutor

public static ExecutorService newSingleThreadExecutor() {
return new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}

Worker线程管理:

private final class Worker extends AbstractQueuedSynchronizer 
implements Runnable {
final Thread thread;
Runnable firstTask;

public void run() {
runWorker(this);
}
}

优势:

  • 降低资源消耗(线程复用)
  • 提高响应速度(无需创建线程)
  • 提高线程的可管理性

128. Redis 数据过期后的删除策略是什么?

展开 中等 VIP 后端 Redis

Redis过期删除策略:

1. 惰性删除(Lazy Expiration)

  • 访问时检查是否过期
  • CPU友好,但可能浪费内存
// 简化的惰性删除逻辑
robj *lookupKeyRead(redisDb *db, robj *key) {
robj *val = lookupKeyReadWithFlags(db, key, LOOKUP_NONE);
if (val == NULL)
return NULL;
if (expireIfNeeded(db, key) == 1) {
return NULL; // 已过期删除
}
return val;
}

2. 定期删除(Periodic Expiration)

  • 默认每秒执行10次(100ms一次)
  • 随机检查部分过期键
  • 平衡CPU和内存使用

定期删除算法:

1. 从expire字典中随机选择20个key
2. 删除其中已过期的key
3. 如果过期key比例 > 25%,重复步骤1
4. 单次执行时间不超过25ms

配置参数:

# redis.conf
hz 10 # 定期删除频率(每秒执行次数)

过期数据结构:

typedef struct redisDb {
dict *dict; // 数据字典
dict *expires; // 过期字典,存储过期时间
// ...
} redisDb;

过期时间设置:

SET key value
EXPIRE key 3600 # 设置过期时间(秒)
EXPIREAT key 1640995200 # 设置过期时间戳
TTL key # 查看剩余时间
PERSIST key # 移除过期时间

内存不足时的处理: 当内存使用达到maxmemory限制时,会触发内存淘汰策略:

  • noeviction:不淘汰,新写入报错
  • allkeys-lru:所有key中LRU淘汰
  • allkeys-lfu:所有key中LFU淘汰
  • volatile-lru:过期key中LRU淘汰
  • volatile-lfu:过期key中LFU淘汰
  • allkeys-random:所有key中随机淘汰
  • volatile-random:过期key中随机淘汰
  • volatile-ttl:过期key中TTL最小的先淘汰

监控过期删除:

INFO stats
# expired_keys: 已过期删除的key数量
# evicted_keys: 因内存不足淘汰的key数量

最佳实践:

  • 合理设置过期时间
  • 避免大量key同时过期
  • 监控过期删除性能
  • 根据业务选择淘汰策略

129. 如何处理消息堆积?

展开 中等 VIP 消息队列 后端

消息堆积原因分析:

1. 生产速度 > 消费速度

  • 流量突增
  • 消费者处理能力不足
  • 消费者故障

2. 消费者问题

  • 消费逻辑耗时过长
  • 消费线程数不够
  • 频繁抛异常重试

解决方案:

1. 临时扩容消费者

// 增加消费者实例
@RabbitListener(queues = "order-queue", concurrency = "5-10")
public void handleMessage(OrderMessage message) {
// 处理逻辑
}

// 或者临时启动更多消费者进程
for (int i = 0; i < 10; i++) {
new Thread(() -> {
// 消费逻辑
}).start();
}

2. 优化消费逻辑

@Component
public class OrderConsumer {
@Async("taskExecutor") // 异步处理
public void processOrder(OrderMessage message) {
// 优化处理逻辑
// 减少数据库查询
// 使用批量操作
}
}

3. 批量消费

@RabbitListener(queues = "order-queue")
public void handleBatch(List<OrderMessage> messages) {
// 批量处理提高效率
batchProcessOrders(messages);
}

4. 消息分流

// 根据消息优先级分流
public class MessageRouter {
public void routeMessage(Message message) {
if (message.isHighPriority()) {
highPriorityQueue.send(message);
} else {
normalQueue.send(message);
}
}
}

5. 降级处理

@Component
public class OrderProcessor {
public void processOrder(OrderMessage message) {
try {
// 正常处理逻辑
normalProcess(message);
} catch (Exception e) {
// 降级处理:简化逻辑或异步处理
degradeProcess(message);
}
}

private void degradeProcess(OrderMessage message) {
// 只做关键处理,其他逻辑异步
saveToDatabase(message);
asyncTaskQueue.add(message);
}
}

6. 动态调整

@Component
public class DynamicConsumerManager {
@Scheduled(fixedRate = 30000) // 30秒检查一次
public void adjustConsumers() {
long queueSize = getQueueSize();
if (queueSize > THRESHOLD) {
// 增加消费者
scaleUpConsumers();
} else if (queueSize < LOW_THRESHOLD) {
// 减少消费者
scaleDownConsumers();
}
}
}

预防措施:

1. 监控告警

@Component
public class QueueMonitor {
@Scheduled(fixedRate = 60000)
public void monitorQueue() {
long queueSize = rabbitmqAdmin.getQueueInfo("order-queue").getMessageCount();
if (queueSize > ALERT_THRESHOLD) {
alertService.sendAlert("Queue accumulation detected: " + queueSize);
}
}
}

2. 限流保护

@RateLimiter(value = 100, timeout = 1000) // 限制每秒100个请求
public void produceMessage(Message message) {
messageProducer.send(message);
}

3. 容量规划

  • 根据业务峰值设计容量
  • 预留足够的缓冲空间
  • 制定扩容预案

4. 消息过期策略

// 设置消息TTL,避免过期消息堆积
@Bean
public Queue orderQueue() {
Map<String, Object> args = new HashMap<>();
args.put("x-message-ttl", 3600000); // 1小时过期
return new Queue("order-queue", true, false, false, args);
}

130. 什么是服务熔断?

展开 中等 VIP Spring Cloud 服务熔断 后端 微服务

服务熔断定义: 服务熔断是一种保护机制,当依赖的服务出现故障时,快速失败并返回默认结果,避免故障扩散。

熔断器状态:

1. 关闭状态(Closed)

  • 正常调用远程服务
  • 统计失败率和响应时间
  • 失败率超过阈值时转为开启状态

2. 开启状态(Open)

  • 直接返回失败或默认值
  • 不调用远程服务
  • 经过一定时间后转为半开状态

3. 半开状态(Half-Open)

  • 允许少量请求通过
  • 根据这些请求的结果决定是否关闭熔断器

Hystrix实现:

@Component
public class UserService {

@HystrixCommand(
fallbackMethod = "getUserFallback",
commandProperties = {
@HystrixProperty(name = "circuitBreaker.requestVolumeThreshold", value = "20"),
@HystrixProperty(name = "circuitBreaker.errorThresholdPercentage", value = "50"),
@HystrixProperty(name = "circuitBreaker.sleepWindowInMilliseconds", value = "5000")
}
)
public User getUser(Long userId) {
// 调用远程服务
return userClient.getUser(userId);
}

// 降级方法
public User getUserFallback(Long userId) {
return User.builder()
.id(userId)
.name("默认用户")
.build();
}
}

Resilience4j实现:

@Component
public class UserService {
private final CircuitBreaker circuitBreaker;

public UserService() {
this.circuitBreaker = CircuitBreaker.ofDefaults("userService");
}

public User getUser(Long userId) {
Supplier<User> decoratedSupplier = CircuitBreaker
.decorateSupplier(circuitBreaker, () -> userClient.getUser(userId));

return Try.ofSupplier(decoratedSupplier)
.recover(throwable -> getUserFallback(userId))
.get();
}
}

Spring Cloud Circuit Breaker:

@RestController
public class UserController {

@Autowired
private CircuitBreakerFactory circuitBreakerFactory;

@GetMapping("/user/{id}")
public User getUser(@PathVariable Long id) {
CircuitBreaker circuitBreaker = circuitBreakerFactory.create("userService");

return circuitBreaker.run(
() -> userClient.getUser(id),
throwable -> getUserFallback(id)
);
}
}

配置参数:

# application.yml
resilience4j:
circuitbreaker:
instances:
userService:
registerHealthIndicator: true
slidingWindowSize: 10
minimumNumberOfCalls: 5
permittedNumberOfCallsInHalfOpenState: 3
waitDurationInOpenState: 5s
failureRateThreshold: 50
slowCallRateThreshold: 50
slowCallDurationThreshold: 2s

监控指标:

@Component
public class CircuitBreakerMetrics {

@EventListener
public void onCircuitBreakerEvent(CircuitBreakerOnStateTransitionEvent event) {
log.info("Circuit breaker {} transition from {} to {}",
event.getCircuitBreakerName(),
event.getStateTransition().getFromState(),
event.getStateTransition().getToState());
}
}

最佳实践:

  • 合理设置熔断阈值
  • 提供有意义的降级逻辑
  • 监控熔断器状态
  • 与限流、重试配合使用
  • 考虑业务影响设计降级策略

131. Java 线程池有哪些拒绝策略?

展开 中等 Java并发 Java

线程池拒绝策略触发条件:

  • 线程池已关闭
  • 工作队列已满且线程数达到maximumPoolSize

JDK内置拒绝策略:

1. AbortPolicy(默认)

public static class AbortPolicy implements RejectedExecutionHandler {
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
throw new RejectedExecutionException("Task " + r.toString() +
" rejected from " +
e.toString());
}
}
  • 直接抛出RejectedExecutionException异常
  • 阻止系统正常运行

2. CallerRunsPolicy

public static class CallerRunsPolicy implements RejectedExecutionHandler {
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
if (!e.isShutdown()) {
r.run(); // 调用者线程执行任务
}
}
}
  • 由调用者线程执行任务
  • 自然的负反馈机制,降低提交速度

3. DiscardPolicy

public static class DiscardPolicy implements RejectedExecutionHandler {
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
// 静默丢弃任务
}
}
  • 静默丢弃被拒绝的任务
  • 适用于允许任务丢失的场景

4. DiscardOldestPolicy

public static class DiscardOldestPolicy implements RejectedExecutionHandler {
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
if (!e.isShutdown()) {
e.getQueue().poll(); // 丢弃队列头部任务
e.execute(r); // 重新提交当前任务
}
}
}
  • 丢弃队列中最老的任务
  • 然后重新提交当前任务

自定义拒绝策略:

1. 记录日志并丢弃

public class LogDiscardPolicy implements RejectedExecutionHandler {
private static final Logger logger = LoggerFactory.getLogger(LogDiscardPolicy.class);

@Override
public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
logger.warn("Task {} rejected from executor {}", r.toString(), executor.toString());
// 可以进行更多处理,如发送告警、记录监控指标等
}
}

2. 保存到备用队列

public class SaveToBackupQueuePolicy implements RejectedExecutionHandler {
private final BlockingQueue<Runnable> backupQueue;

public SaveToBackupQueuePolicy(BlockingQueue<Runnable> backupQueue) {
this.backupQueue = backupQueue;
}

@Override
public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
try {
backupQueue.put(r); // 保存到备用队列
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
}

3. 动态调整策略

public class DynamicRejectedExecutionHandler implements RejectedExecutionHandler {
private volatile RejectedExecutionHandler currentHandler;

@Override
public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
// 根据系统负载动态选择策略
if (getSystemLoad() > 0.8) {
new DiscardPolicy().rejectedExecution(r, executor);
} else {
new CallerRunsPolicy().rejectedExecution(r, executor);
}
}
}

使用示例:

ThreadPoolExecutor executor = new ThreadPoolExecutor(
2, 4, 60L, TimeUnit.SECONDS,
new ArrayBlockingQueue<>(2),
new CallerRunsPolicy() // 指定拒绝策略
);

选择建议:

  • AbortPolicy:需要感知任务失败的场景
  • CallerRunsPolicy:可以容忍任务执行延迟
  • DiscardPolicy:允许任务丢失,对实时性要求高
  • DiscardOldestPolicy:新任务比老任务重要

132. HTTP 1.0 和 2.0 有什么区别?

展开 中等 网络

主要区别对比:

特性HTTP/1.0HTTP/1.1HTTP/2.0
连接短连接长连接(Keep-Alive)多路复用
请求响应串行串行(管道化可并行)并行
头部压缩HPACK压缩
服务器推送支持
二进制传输文本文本二进制帧

HTTP/1.0 特点:

GET /index.html HTTP/1.0
Host: www.example.com
Connection: close

# 每个请求都要建立新连接

HTTP/1.1 改进:

GET /index.html HTTP/1.1
Host: www.example.com
Connection: keep-alive

# 支持持久连接
# 支持请求管道化
# 增加缓存控制
# 支持断点续传

HTTP/2.0 革新:

1. 二进制分帧

HTTP/1.x: 文本协议
GET /index.html HTTP/1.1\r\n
Host: www.example.com\r\n

HTTP/2: 二进制帧
[Frame Type][Flags][Stream ID][Payload]

2. 多路复用

// HTTP/1.1: 需要多个连接
// 连接1: GET /style.css
// 连接2: GET /script.js
// 连接3: GET /image.png

// HTTP/2: 单个连接多个流
// Stream 1: GET /style.css
// Stream 3: GET /script.js
// Stream 5: GET /image.png
// 可交错发送,无队头阻塞

3. 头部压缩(HPACK)

# HTTP/1.1: 每次都发送完整头部
GET /api/user HTTP/1.1
Host: api.example.com
User-Agent: Mozilla/5.0 ...
Accept: application/json
Cookie: session=abc123...

# HTTP/2: 头部表压缩
:method: GET
:path: /api/user
:authority: api.example.com
# 重复头部用索引表示

4. 服务器推送

// 客户端请求 index.html
// 服务器主动推送 style.css, script.js

// Node.js HTTP/2 服务器推送示例
const http2 = require('http2');

const server = http2.createSecureServer();
server.on('stream', (stream, headers) => {
if (headers[':path'] === '/') {
// 推送资源
stream.pushStream({
':path': '/style.css',
':method': 'GET'
}, (err, pushStream) => {
pushStream.respond({ ':status': 200 });
pushStream.end('/* CSS content */');
});
}
});

5. 流优先级

Stream Priority:
- 依赖关系:Stream B依赖Stream A
- 权重分配:分配带宽权重
- 动态调整:根据需要调整优先级

性能对比:

连接数:

  • HTTP/1.1: 浏览器限制6-8个并发连接
  • HTTP/2: 单个连接处理所有请求

传输效率:

  • HTTP/1.1: 头部冗余,串行传输
  • HTTP/2: 头部压缩,并行传输

延迟:

  • HTTP/1.1: 队头阻塞问题
  • HTTP/2: 解决队头阻塞,降低延迟

兼容性:

  • HTTP/2向下兼容HTTP/1.1
  • 需要HTTPS支持(实际部署中)
  • 现代浏览器广泛支持

迁移建议:

  • 启用HTTPS
  • 服务器支持HTTP/2
  • 优化资源合并策略
  • 利用服务器推送

133. 如何保证消息不丢失?

展开 中等 VIP 消息队列 后端

消息丢失的环节:

  1. 生产者丢失:发送消息到MQ失败
  2. MQ丢失:消息在队列中丢失
  3. 消费者丢失:消费过程中处理失败

生产者层面保证:

1. 发送确认机制

// RabbitMQ 发送确认
@Component
public class OrderProducer {

@Autowired
private RabbitTemplate rabbitTemplate;

public void sendOrder(OrderMessage message) {
// 设置确认回调
rabbitTemplate.setConfirmCallback((correlationData, ack, cause) -> {
if (!ack) {
log.error("Message send failed: {}", cause);
// 重试或报警
retryOrAlert(message);
}
});

// 设置返回回调(路由失败)
rabbitTemplate.setReturnsCallback((returned) -> {
log.error("Message returned: {}", returned.getReplyText());
// 处理路由失败
});

rabbitTemplate.convertAndSend("order.exchange", "order.create", message);
}
}

2. 事务机制

// RocketMQ 事务消息
@Component
public class TransactionalProducer {

public void sendTransactionalMessage(OrderMessage message) {
// 发送half消息
SendResult result = producer.sendMessageInTransaction(message, null);

if (result.getSendStatus() == SendStatus.SEND_OK) {
// 执行本地事务
boolean success = executeLocalTransaction(message);

if (success) {
// 提交事务
producer.endTransaction(SendStatus.COMMIT_MESSAGE);
} else {
// 回滚事务
producer.endTransaction(SendStatus.ROLLBACK_MESSAGE);
}
}
}
}

MQ层面保证:

1. 消息持久化

# RabbitMQ 持久化配置
rabbitmq:
queue:
durable: true # 队列持久化
message-ttl: 3600000 # 消息TTL
exchange:
durable: true # 交换机持久化

2. 集群部署

# RabbitMQ 镜像队列
rabbitmq:
ha-mode: all
ha-sync-mode: automatic

3. 磁盘写入

// Kafka 配置
Properties props = new Properties();
props.put("acks", "all"); // 等待所有副本确认
props.put("retries", 3); // 重试次数
props.put("batch.size", 16384);
props.put("linger.ms", 1);
props.put("buffer.memory", 33554432);

消费者层面保证:

1. 手动确认机制

@RabbitListener(queues = "order.queue")
public void handleOrder(OrderMessage message, Channel channel,
@Header(AmqpHeaders.DELIVERY_TAG) long deliveryTag) {
try {
// 处理业务逻辑
orderService.processOrder(message);

// 手动确认
channel.basicAck(deliveryTag, false);
} catch (BusinessException e) {
// 业务异常,拒绝消息并重新入队
channel.basicReject(deliveryTag, true);
} catch (Exception e) {
// 系统异常,拒绝消息不重新入队
channel.basicReject(deliveryTag, false);
}
}

2. 幂等性处理

@Service
public class OrderService {

@Transactional
public void processOrder(OrderMessage message) {
String orderId = message.getOrderId();

// 检查是否已处理
if (orderRepository.existsById(orderId)) {
log.info("Order {} already processed", orderId);
return;
}

// 处理订单
Order order = new Order(message);
orderRepository.save(order);

// 记录处理状态
processLogRepository.save(new ProcessLog(orderId, ProcessStatus.SUCCESS));
}
}

3. 死信队列

@Configuration
public class DeadLetterConfig {

@Bean
public Queue orderQueue() {
Map<String, Object> args = new HashMap<>();
args.put("x-dead-letter-exchange", "dlx.exchange");
args.put("x-dead-letter-routing-key", "dlx.order");
args.put("x-message-ttl", 60000); // 1分钟TTL
args.put("x-max-retries", 3); // 最大重试次数

return new Queue("order.queue", true, false, false, args);
}

@RabbitListener(queues = "dlx.order.queue")
public void handleDeadLetter(OrderMessage message) {
log.error("Message processing failed permanently: {}", message);
// 人工处理或报警
alertService.sendAlert("Dead letter message", message);
}
}

端到端保证方案:

1. 消息表模式

@Transactional
public void createOrderWithMessage(OrderRequest request) {
// 1. 创建订单
Order order = orderRepository.save(new Order(request));

// 2. 在同一事务中保存消息记录
MessageLog messageLog = new MessageLog(order.getId(), "ORDER_CREATED", order);
messageLogRepository.save(messageLog);

// 3. 异步发送消息
asyncMessageSender.sendMessage(messageLog);
}

// 定时任务扫描未发送成功的消息
@Scheduled(fixedRate = 30000)
public void resendFailedMessages() {
List<MessageLog> failedMessages = messageLogRepository.findFailedMessages();
for (MessageLog message : failedMessages) {
try {
messageProducer.send(message);
message.markAsSent();
} catch (Exception e) {
message.incrementRetryCount();
}
messageLogRepository.save(message);
}
}

2. Saga模式

@Component
public class OrderSaga {

public void processOrder(OrderMessage message) {
try {
// 步骤1:扣减库存
inventoryService.deductStock(message.getProductId());

// 步骤2:创建订单
orderService.createOrder(message);

// 步骤3:支付
paymentService.processPayment(message);

} catch (Exception e) {
// 补偿操作
compensate(message);
}
}

private void compensate(OrderMessage message) {
// 回滚库存
inventoryService.addStock(message.getProductId());
// 取消订单
orderService.cancelOrder(message.getOrderId());
}
}

监控和告警:

@Component
public class MessageMonitor {

@EventListener
public void onMessageSent(MessageSentEvent event) {
messageMetrics.incrementSentCounter();
}

@EventListener
public void onMessageFailed(MessageFailedEvent event) {
messageMetrics.incrementFailedCounter();
if (event.isCritical()) {
alertService.sendCriticalAlert(event);
}
}
}

134. Spring AOP默认用的是什么动态代理,两者的区别?

展开 中等 VIP 后端 Spring

Spring AOP代理选择策略:

默认规则:

  • 如果目标对象实现了接口 → JDK动态代理
  • 如果目标对象没有实现接口 → CGLIB代理
  • 可以强制使用CGLIB代理

JDK动态代理:

实现原理:

// JDK动态代理示例
public class JdkProxyExample {
public static void main(String[] args) {
UserService target = new UserServiceImpl();

UserService proxy = (UserService) Proxy.newProxyInstance(
target.getClass().getClassLoader(),
target.getClass().getInterfaces(),
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("Before method: " + method.getName());
Object result = method.invoke(target, args);
System.out.println("After method: " + method.getName());
return result;
}
}
);

proxy.getUser(1L);
}
}

特点:

  • 基于接口代理
  • JDK内置,无需额外依赖
  • 代理对象实现相同接口
  • 性能较好

CGLIB代理:

实现原理:

// CGLIB代理示例
public class CglibProxyExample {
public static void main(String[] args) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(UserService.class);
enhancer.setCallback(new MethodInterceptor() {
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
System.out.println("Before method: " + method.getName());
Object result = proxy.invokeSuper(obj, args);
System.out.println("After method: " + method.getName());
return result;
}
});

UserService proxy = (UserService) enhancer.create();
proxy.getUser(1L);
}
}

特点:

  • 基于继承代理(生成子类)
  • 需要额外的CGLIB依赖
  • 不能代理final类和方法
  • 创建代理对象较慢,但执行快

区别对比:

特性JDK动态代理CGLIB代理
实现方式接口代理继承代理
要求必须有接口无接口要求
性能创建快,调用稍慢创建慢,调用快
限制只能代理接口方法不能代理final类/方法
依赖JDK内置需要CGLIB库
代理对象类型接口类型目标类的子类

Spring配置:

强制使用CGLIB:

@Configuration
@EnableAspectJAutoProxy(proxyTargetClass = true)
public class AopConfig {
// proxyTargetClass = true 强制使用CGLIB
}
# application.yml
spring:
aop:
proxy-target-class: true # 强制使用CGLIB

AOP代理示例:

接口和实现:

public interface UserService {
User getUser(Long id);
}

@Service
public class UserServiceImpl implements UserService {
@Override
public User getUser(Long id) {
return new User(id, "张三");
}
}

切面定义:

@Aspect
@Component
public class LoggingAspect {

@Around("@annotation(org.springframework.transaction.annotation.Transactional)")
public Object logExecutionTime(ProceedingJoinPoint joinPoint) throws Throwable {
long start = System.currentTimeMillis();
try {
return joinPoint.proceed();
} finally {
long executionTime = System.currentTimeMillis() - start;
log.info("{} executed in {} ms",
joinPoint.getSignature().getName(), executionTime);
}
}
}

代理类型判断:

@Component
public class ProxyChecker {

@Autowired
private UserService userService;

@PostConstruct
public void checkProxyType() {
if (AopUtils.isJdkDynamicProxy(userService)) {
log.info("Using JDK Dynamic Proxy");
} else if (AopUtils.isCglibProxy(userService)) {
log.info("Using CGLIB Proxy");
}

// 获取目标对象
Object target = AopTestUtils.getTargetObject(userService);
log.info("Target class: {}", target.getClass().getName());
}
}

性能考虑:

JDK代理性能特点:

  • 代理对象创建速度快
  • 方法调用有反射开销
  • 适合调用频率不高的场景

CGLIB代理性能特点:

  • 代理对象创建速度慢(字节码生成)
  • 方法调用速度快(直接调用)
  • 适合高频调用的场景

选择建议:

  • 默认策略通常已经足够
  • 高性能要求可考虑CGLIB
  • 接口设计良好时使用JDK代理
  • 遗留代码无接口时使用CGLIB

135. 如何合理地设置 Java 线程池的线程数?

展开 中等 Java并发 Java

理论计算公式:

CPU密集型任务:

线程数 = CPU核心数 + 1
  • +1是为了在某个线程暂停时能够充分利用CPU
  • 更多线程会导致上下文切换开销

I/O密集型任务:

线程数 = CPU核心数 / (1 - 阻塞系数)

线程数 = CPU核心数 × (1 + I/O耗时/CPU耗时)

实际评估方法:

1. 性能测试确定

@Component
public class ThreadPoolTuner {

public void findOptimalThreadCount() {
int coreSize = Runtime.getRuntime().availableProcessors();

// 测试不同线程数的性能
for (int threads = coreSize; threads <= coreSize * 4; threads++) {
ThreadPoolExecutor executor = new ThreadPoolExecutor(
threads, threads, 0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<>()
);

long startTime = System.currentTimeMillis();

// 提交测试任务
List<Future<?>> futures = new ArrayList<>();
for (int i = 0; i < 1000; i++) {
futures.add(executor.submit(() -> simulateTask()));
}

// 等待完成
futures.forEach(future -> {
try {
future.get();
} catch (Exception e) {
e.printStackTrace();
}
});

long endTime = System.currentTimeMillis();
System.out.printf("Threads: %d, Time: %d ms%n",
threads, endTime - startTime);

executor.shutdown();
}
}
}

2. 监控指标分析

@Component
public class ThreadPoolMonitor {

@Scheduled(fixedRate = 30000)
public void monitorThreadPool() {
ThreadPoolExecutor executor = (ThreadPoolExecutor) this.executor;

int activeCount = executor.getActiveCount();
int poolSize = executor.getPoolSize();
long completedTaskCount = executor.getCompletedTaskCount();
int queueSize = executor.getQueue().size();

// 计算利用率
double utilization = (double) activeCount / poolSize;

log.info("Thread Pool Stats - Active: {}, Pool Size: {}, " +
"Queue Size: {}, Utilization: {:.2f}",
activeCount, poolSize, queueSize, utilization);

// 自动调整策略
if (utilization > 0.8 && queueSize > 100) {
// 考虑增加线程数
adjustThreadPool(poolSize + 2);
} else if (utilization < 0.2 && poolSize > corePoolSize) {
// 考虑减少线程数
adjustThreadPool(Math.max(corePoolSize, poolSize - 2));
}
}
}

不同场景的配置建议:

1. Web应用服务器

@Configuration
public class WebThreadPoolConfig {

@Bean("webTaskExecutor")
public ThreadPoolExecutor webTaskExecutor() {
int coreSize = Runtime.getRuntime().availableProcessors();

return new ThreadPoolExecutor(
coreSize * 2, // 核心线程数
coreSize * 4, // 最大线程数
60L, // 空闲时间
TimeUnit.SECONDS,
new ArrayBlockingQueue<>(200), // 有界队列
new CustomThreadFactory("web-task"),
new CallerRunsPolicy()
);
}
}

2. 数据库访问

@Configuration
public class DatabaseThreadPoolConfig {

@Bean("dbTaskExecutor")
public ThreadPoolExecutor dbTaskExecutor() {
// 数据库连接池大小通常限制线程数
int maxDbConnections = 20;

return new ThreadPoolExecutor(
10, // 核心线程数
maxDbConnections, // 不超过数据库连接数
300L,
TimeUnit.SECONDS,
new LinkedBlockingQueue<>(100),
new CustomThreadFactory("db-task"),
new CallerRunsPolicy()
);
}
}

3. 文件处理

@Configuration
public class FileProcessingConfig {

@Bean("fileTaskExecutor")
public ThreadPoolExecutor fileTaskExecutor() {
int coreSize = Runtime.getRuntime().availableProcessors();

return new ThreadPoolExecutor(
coreSize, // I/O密集型,线程数可以更多
coreSize * 3,
120L,
TimeUnit.SECONDS,
new ArrayBlockingQueue<>(50),
new CustomThreadFactory("file-task"),
new AbortPolicy() // 文件处理失败要感知
);
}
}

动态调整线程池

@Component
public class DynamicThreadPoolManager {

private final ThreadPoolExecutor executor;
private final ScheduledExecutorService scheduler;

public DynamicThreadPoolManager() {
this.executor = createInitialThreadPool();
this.scheduler = Executors.newScheduledThreadPool(1);

// 定期调整
scheduler.scheduleAtFixedRate(this::adjustThreadPool,
1, 1, TimeUnit.MINUTES);
}

private void adjustThreadPool() {
// 获取系统负载
double systemLoad = ManagementFactory.getOperatingSystemMXBean()
.getSystemLoadAverage();

// 获取线程池指标
int activeCount = executor.getActiveCount();
int poolSize = executor.getPoolSize();
int queueSize = executor.getQueue().size();

// 调整策略
if (systemLoad > 0.8 || queueSize > 100) {
// 系统负载高,增加线程
int newSize = Math.min(poolSize + 2, getMaxAllowedThreads());
executor.setMaximumPoolSize(newSize);
executor.setCorePoolSize(Math.min(newSize, executor.getCorePoolSize()));
} else if (systemLoad < 0.3 && activeCount < poolSize * 0.5) {
// 系统负载低,减少线程
int newSize = Math.max(poolSize - 1, getMinRequiredThreads());
executor.setCorePoolSize(newSize);
executor.setMaximumPoolSize(Math.max(newSize, executor.getMaximumPoolSize()));
}
}
}

配置最佳实践:

1. 根据任务特点调整

// CPU密集型
int cpuIntensiveThreads = Runtime.getRuntime().availableProcessors() + 1;

// I/O密集型(网络调用)
int ioIntensiveThreads = Runtime.getRuntime().availableProcessors() * 2;

// 混合型任务
int mixedThreads = (int)(Runtime.getRuntime().availableProcessors() * 1.5);

2. 考虑业务约束

  • 数据库连接池大小
  • 外部API限流
  • 内存使用限制
  • 响应时间要求

3. 监控和预警

@Component
public class ThreadPoolAlerts {

@EventListener
public void onThreadPoolFull(ThreadPoolFullEvent event) {
if (event.getQueueSize() > ALERT_THRESHOLD) {
alertService.sendAlert("Thread pool queue is full", event);
}
}

@Scheduled(fixedRate = 60000)
public void checkThreadPoolHealth() {
double rejectionRate = getRejectionRate();
if (rejectionRate > 0.05) { // 5%拒绝率
alertService.sendAlert("High rejection rate: " + rejectionRate);
}
}
}

调优步骤:

  1. 分析任务特点(CPU/I/O密集型)
  2. 压力测试找到最优值
  3. 监控生产环境指标
  4. 根据业务增长调整
  5. 设置合理的告警阈值

136. Redis 中有哪些内存淘汰策略?

展开 中等 VIP 后端 Redis

内存淘汰触发条件: 当Redis内存使用量达到maxmemory限制时,会根据配置的策略淘汰数据。

八种淘汰策略:

1. noeviction(默认)

# 不淘汰任何数据
# 新写入操作会报错
CONFIG SET maxmemory-policy noeviction
  • 禁止驱逐数据
  • 新写入命令返回错误
  • 适合缓存不允许丢失的场景

2. allkeys-lru

# 对所有key使用LRU算法淘汰
CONFIG SET maxmemory-policy allkeys-lru
  • 从所有数据集中挑选最近最少使用的数据淘汰
  • 最常用的策略

3. allkeys-lfu

# 对所有key使用LFU算法淘汰
CONFIG SET maxmemory-policy allkeys-lfu
  • 从所有数据集中挑选最不经常使用的数据淘汰
  • Redis 4.0+支持

4. volatile-lru

# 对设置了过期时间的key使用LRU算法
CONFIG SET maxmemory-policy volatile-lru
  • 只从设置了过期时间的数据集中挑选LRU数据淘汰

5. volatile-lfu

# 对设置了过期时间的key使用LFU算法
CONFIG SET maxmemory-policy volatile-lfu
  • 只从设置了过期时间的数据集中挑选LFU数据淘汰

6. allkeys-random

# 从所有key中随机淘汰
CONFIG SET maxmemory-policy allkeys-random
  • 从所有数据集中任意选择数据淘汰

7. volatile-random

# 从设置了过期时间的key中随机淘汰
CONFIG SET maxmemory-policy volatile-random
  • 从设置了过期时间的数据集中任意选择数据淘汰

8. volatile-ttl

# 淘汰TTL最小的key
CONFIG SET maxmemory-policy volatile-ttl
  • 从设置了过期时间的数据集中挑选将要过期的数据淘汰

LRU vs LFU算法:

LRU(Least Recently Used)

// Redis LRU实现(简化)
typedef struct redisObject {
unsigned lru:LRU_BITS; // 最后访问时间
// ... other fields
} robj;

// 更新访问时间
void updateLRU(robj *o) {
o->lru = LRU_CLOCK();
}

LFU(Least Frequently Used)

// Redis LFU实现(简化)
typedef struct redisObject {
unsigned lru:LRU_BITS; // 在LFU模式下存储频率信息
// lru字段被重新解释:
// 高16位:最后递减时间
// 低8位:访问频率
} robj;

配置示例:

# redis.conf
maxmemory 2gb
maxmemory-policy allkeys-lru
maxmemory-samples 5 # LRU样本数量,影响精度

Java客户端配置:

@Configuration
public class RedisConfig {

@Bean
public JedisPool jedisPool() {
JedisPoolConfig config = new JedisPoolConfig();
config.setMaxTotal(100);

return new JedisPool(config, "localhost", 6379);
}

@PostConstruct
public void configureEviction() {
try (Jedis jedis = jedisPool.getResource()) {
// 设置最大内存
jedis.configSet("maxmemory", "1gb");

// 设置淘汰策略
jedis.configSet("maxmemory-policy", "allkeys-lru");

// 设置LRU样本数
jedis.configSet("maxmemory-samples", "10");
}
}
}

监控内存使用:

@Component
public class RedisMemoryMonitor {

@Scheduled(fixedRate = 30000)
public void monitorMemory() {
try (Jedis jedis = jedisPool.getResource()) {
String info = jedis.info("memory");

// 解析内存信息
Map<String, String> memoryInfo = parseInfo(info);

long usedMemory = Long.parseLong(memoryInfo.get("used_memory"));
long maxMemory = Long.parseLong(memoryInfo.get("maxmemory"));

double memoryUsage = (double) usedMemory / maxMemory;

if (memoryUsage > 0.8) {
log.warn("Redis memory usage is high: {:.2f}%", memoryUsage * 100);
}

// 记录淘汰统计
long evictedKeys = Long.parseLong(memoryInfo.get("evicted_keys"));
log.info("Evicted keys: {}", evictedKeys);
}
}
}

策略选择建议:

场景分析:

public class EvictionPolicySelector {

public String selectPolicy(CacheScenario scenario) {
switch (scenario) {
case GENERAL_CACHE:
return "allkeys-lru"; // 通用缓存推荐

case SESSION_STORE:
return "volatile-lru"; // 会话存储,有TTL

case HOT_DATA_CACHE:
return "allkeys-lfu"; // 热点数据,访问频率重要

case TEMPORARY_CACHE:
return "volatile-ttl"; // 临时缓存,优先淘汰快过期的

case CRITICAL_CACHE:
return "noeviction"; // 关键缓存,不允许淘汰

default:
return "allkeys-lru";
}
}
}

性能对比:

策略计算复杂度内存开销适用场景
LRUO(1)通用场景
LFUO(1)中等热点数据明显
RandomO(1)最低对精度要求不高
TTLO(log N)有明确过期需求

最佳实践:

  1. 根据业务特点选择策略
  2. 监控淘汰频率和命中率
  3. 合理设置maxmemory值
  4. 考虑数据的访问模式
  5. 定期评估和调整策略

137. MySQL 中 如果我 select * from 一个有 1000 万行的表,内存会飙升么?

展开 困难 VIP 后端 场景题

答案:会,但取决于具体的执行方式和配置。

内存消耗分析:

1. MySQL服务器端:

-- 大表查询会占用多种内存
SELECT * FROM large_table; -- 1000万行

-- 涉及的内存区域:
-- 1. InnoDB Buffer Pool:缓存数据页
-- 2. Sort Buffer:排序操作
-- 3. Join Buffer:表连接
-- 4. Query Cache:查询缓存(MySQL 8.0已移除)
-- 5. 临时表:GROUP BY、ORDER BY等操作

2. 客户端内存消耗:

JDBC默认行为(全量加载):

// 危险:会将所有结果加载到内存
public class MemoryProblemExample {
public void badExample() {
String sql = "SELECT * FROM large_table";
try (PreparedStatement stmt = connection.prepareStatement(sql);
ResultSet rs = stmt.executeQuery()) {

List<Record> records = new ArrayList<>(); // 内存炸弹
while (rs.next()) {
records.add(new Record(rs)); // 1000万个对象
}
}
}
}

解决方案:

1. 流式查询(Streaming)

public class StreamingQueryExample {

public void streamingQuery() throws SQLException {
String sql = "SELECT * FROM large_table";

// MySQL JDBC流式查询配置
PreparedStatement stmt = connection.prepareStatement(
sql,
ResultSet.TYPE_FORWARD_ONLY,
ResultSet.CONCUR_READ_ONLY
);

// 关键配置
stmt.setFetchSize(Integer.MIN_VALUE); // MySQL特殊值,启用流式

try (ResultSet rs = stmt.executeQuery()) {
while (rs.next()) {
// 逐行处理,不缓存
processRecord(rs);

// 定期释放内存
if (count % 10000 == 0) {
System.gc(); // 建议垃圾回收
}
}
}
}
}

2. 分页查询

public class PaginatedQueryExample {

public void paginatedQuery() {
int pageSize = 1000;
int offset = 0;

while (true) {
String sql = "SELECT * FROM large_table LIMIT ? OFFSET ?";

try (PreparedStatement stmt = connection.prepareStatement(sql)) {
stmt.setInt(1, pageSize);
stmt.setInt(2, offset);

try (ResultSet rs = stmt.executeQuery()) {
List<Record> batch = new ArrayList<>();
while (rs.next()) {
batch.add(new Record(rs));
}

if (batch.isEmpty()) {
break; // 没有更多数据
}

processBatch(batch); // 处理批次
offset += pageSize;
}
} catch (SQLException e) {
log.error("Query failed", e);
break;
}
}
}
}

3. 游标查询

public class CursorQueryExample {

public void cursorQuery() throws SQLException {
connection.setAutoCommit(false); // 必须在事务中

String sql = "SELECT * FROM large_table";
PreparedStatement stmt = connection.prepareStatement(sql);
stmt.setFetchSize(1000); // 设置每次获取行数

try (ResultSet rs = stmt.executeQuery()) {
while (rs.next()) {
processRecord(rs);
}
} finally {
connection.setAutoCommit(true);
}
}
}

4. 使用游标和临时表

-- 存储过程中使用游标
DELIMITER $$
CREATE PROCEDURE ProcessLargeTable()
BEGIN
DECLARE done INT DEFAULT FALSE;
DECLARE cur CURSOR FOR SELECT * FROM large_table;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE;

OPEN cur;

read_loop: LOOP
FETCH cur INTO @col1, @col2, @col3; -- 根据实际列调整

IF done THEN
LEAVE read_loop;
END IF;

-- 处理当前行
CALL process_record(@col1, @col2, @col3);
END LOOP;

CLOSE cur;
END$$
DELIMITER ;

内存监控:

@Component
public class MemoryMonitor {

public void monitorQueryMemory() {
MemoryMXBean memoryBean = ManagementFactory.getMemoryMXBean();

// 查询前的内存状态
long beforeUsed = memoryBean.getHeapMemoryUsage().getUsed();

// 执行查询
executeQuery();

// 查询后的内存状态
long afterUsed = memoryBean.getHeapMemoryUsage().getUsed();

log.info("Memory increase: {} MB",
(afterUsed - beforeUsed) / 1024 / 1024);
}

@Scheduled(fixedRate = 30000)
public void checkMemoryUsage() {
MemoryUsage heapUsage = ManagementFactory.getMemoryMXBean()
.getHeapMemoryUsage();

double usagePercent = (double) heapUsage.getUsed() / heapUsage.getMax();

if (usagePercent > 0.8) {
log.warn("High memory usage: {:.2f}%", usagePercent * 100);
}
}
}

数据库层面优化:

1. 索引优化

-- 确保查询有合适的索引
EXPLAIN SELECT * FROM large_table WHERE condition;

-- 创建必要的索引
CREATE INDEX idx_condition ON large_table(condition_column);

2. 分区表

-- 创建分区表减少单次扫描数据量
CREATE TABLE large_table_partitioned (
id INT,
created_date DATE,
data TEXT
) PARTITION BY RANGE (YEAR(created_date)) (
PARTITION p2020 VALUES LESS THAN (2021),
PARTITION p2021 VALUES LESS THAN (2022),
PARTITION p2022 VALUES LESS THAN (2023),
PARTITION p2023 VALUES LESS THAN (2024)
);

3. 查询重写

-- 避免 SELECT *,只查询需要的列
SELECT id, name, status FROM large_table;

-- 使用条件过滤
SELECT * FROM large_table WHERE created_date >= '2023-01-01';

-- 使用聚合减少返回行数
SELECT COUNT(*), category FROM large_table GROUP BY category;

配置优化:

MySQL配置:

# my.cnf
[mysqld]
innodb_buffer_pool_size = 2G # 增加缓冲池
max_connections = 200 # 限制连接数
query_cache_size = 0 # 禁用查询缓存(大结果集)
tmp_table_size = 256M # 临时表大小
max_heap_table_size = 256M # 内存表大小

JVM配置:

# 增加堆内存
-Xms2g -Xmx4g

# 优化垃圾回收
-XX:+UseG1GC
-XX:MaxGCPauseMillis=200

# 监控内存使用
-XX:+HeapDumpOnOutOfMemoryError
-XX:HeapDumpPath=/tmp/heap_dump.hprof

最佳实践:

  1. 避免全表扫描大表
  2. 使用适当的查询方式(流式/分页)
  3. 监控内存使用情况
  4. 优化查询条件和索引
  5. 考虑数据归档和分区
  6. 设置合理的超时时间

138. 消息队列设计成推消息还是拉消息?推拉模式的优缺点?

展开 中等 VIP 消息队列 后端 场景题

推拉模式对比:

推模式(Push Model): 服务器主动将消息推送给消费者

拉模式(Pull Model): 消费者主动从服务器拉取消息

推模式详解:

实现方式:

// 推模式示例 - RabbitMQ
@RabbitListener(queues = "order.queue")
public void handleMessage(OrderMessage message) {
// 消息被推送到这个方法
processOrder(message);
}

// WebSocket推送示例
@Component
public class MessagePusher {

@Autowired
private SimpMessagingTemplate messagingTemplate;

public void pushMessage(String userId, Object message) {
messagingTemplate.convertAndSend("/topic/user/" + userId, message);
}
}

推模式优点:

  • 实时性好:消息到达立即推送
  • 实现简单:消费者被动接收
  • 低延迟:无需轮询等待
  • 资源利用高:无无效轮询

推模式缺点:

  • 消费者压力大:无法控制推送速度
  • 缓冲区溢出:消费者处理不及时可能导致内存问题
  • 流量控制难:难以实现消费者侧的流控
  • 错误处理复杂:推送失败的处理逻辑复杂

拉模式详解:

实现方式:

// 拉模式示例 - Kafka
@Component
public class KafkaConsumer {

private final ConsumerFactory<String, String> consumerFactory;

@Scheduled(fixedRate = 1000) // 定时拉取
public void pullMessages() {
Consumer<String, String> consumer = consumerFactory.createConsumer();
consumer.subscribe(Arrays.asList("order-topic"));

ConsumerRecords<String, String> records = consumer.poll(Duration.ofMillis(1000));

for (ConsumerRecord<String, String> record : records) {
processMessage(record.value());
}

consumer.commitSync(); // 手动提交偏移量
consumer.close();
}
}

// 手动拉取示例
@Component
public class ManualPuller {

public void pullLoop() {
while (running) {
try {
List<Message> messages = messageQueue.poll(batchSize, timeout);

if (!messages.isEmpty()) {
processBatch(messages);
} else {
// 没有消息,短暂休眠
Thread.sleep(100);
}
} catch (Exception e) {
log.error("Pull failed", e);
Thread.sleep(1000); // 错误后等待更长时间
}
}
}
}

拉模式优点:

  • 流量控制:消费者控制消费速度
  • 简单可靠:消费者状态易于管理
  • 批量处理:可以批量拉取提高效率
  • 错误隔离:拉取失败不影响其他消费者

拉模式缺点:

  • 延迟较高:需要轮询等待
  • 资源浪费:空轮询消耗CPU
  • 实现复杂:需要处理轮询逻辑
  • 实时性差:依赖轮询间隔

混合模式:

长轮询(Long Polling):

@RestController
public class LongPollingController {

private final Map<String, DeferredResult<List<Message>>> deferredResults = new ConcurrentHashMap<>();

@GetMapping("/messages/poll")
public DeferredResult<List<Message>> pollMessages(@RequestParam String userId) {
DeferredResult<List<Message>> deferredResult = new DeferredResult<>(30000L);

// 立即检查是否有消息
List<Message> messages = messageService.getMessages(userId);
if (!messages.isEmpty()) {
deferredResult.setResult(messages);
return deferredResult;
}

// 没有消息,保存请求等待推送
deferredResults.put(userId, deferredResult);

// 超时处理
deferredResult.onTimeout(() -> {
deferredResults.remove(userId);
deferredResult.setResult(Collections.emptyList());
});

return deferredResult;
}

// 当有新消息时调用
public void notifyNewMessage(String userId, List<Message> messages) {
DeferredResult<List<Message>> deferredResult = deferredResults.remove(userId);
if (deferredResult != null) {
deferredResult.setResult(messages);
}
}
}

事件驱动的拉取:

@Component
public class EventDrivenPuller {

@EventListener
public void onMessageArrival(MessageArrivalEvent event) {
// 有消息到达时立即拉取
pullMessages(event.getQueueName());
}

@Scheduled(fixedRate = 5000) // 兜底轮询
public void scheduledPull() {
// 定期检查是否有遗漏的消息
pullMessages("order-queue");
}
}

不同MQ的选择:

RabbitMQ(推模式为主):

@Configuration
@EnableRabbit
public class RabbitConfig {

@Bean
public SimpleRabbitListenerContainerFactory rabbitListenerContainerFactory() {
SimpleRabbitListenerContainerFactory factory = new SimpleRabbitListenerContainerFactory();
factory.setConnectionFactory(connectionFactory());
factory.setPrefetchCount(10); // 控制推送速度
factory.setConcurrentConsumers(2);
factory.setMaxConcurrentConsumers(5);
return factory;
}
}

Kafka(拉模式为主):

@Configuration
public class KafkaConfig {

@Bean
public ConcurrentKafkaListenerContainerFactory<String, String> kafkaListenerContainerFactory() {
ConcurrentKafkaListenerContainerFactory<String, String> factory = new ConcurrentKafkaListenerContainerFactory<>();
factory.setConsumerFactory(consumerFactory());
factory.setConcurrency(3); // 并发消费者数量
factory.getContainerProperties().setPollTimeout(3000); // 拉取超时
return factory;
}
}

选择建议:

推模式适用场景:

  • 实时性要求高的系统
  • 消费者处理能力稳定
  • 消息量相对较小
  • 简单的消费逻辑

拉模式适用场景:

  • 消费者需要控制消费速度
  • 批量处理需求
  • 消费者处理能力不稳定
  • 高吞吐量场景

实际应用建议:

@Component
public class HybridMessageConsumer {

// 高优先级消息使用推模式
@RabbitListener(queues = "high-priority-queue")
public void handleHighPriorityMessage(HighPriorityMessage message) {
processHighPriority(message);
}

// 批量处理使用拉模式
@Scheduled(fixedRate = 5000)
public void pullBatchMessages() {
List<BatchMessage> messages = kafkaTemplate.receive("batch-topic", 100);
processBatch(messages);
}

// 使用长轮询优化拉模式
public List<Message> longPoll(String queueName, long timeout) {
long startTime = System.currentTimeMillis();
while (System.currentTimeMillis() - startTime < timeout) {
List<Message> messages = pullMessages(queueName);
if (!messages.isEmpty()) {
return messages;
}
try {
Thread.sleep(100); // 短暂等待
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
break;
}
}
return Collections.emptyList();
}
}

性能优化:

  • 推模式:合理设置prefetch、并发度
  • 拉模式:批量拉取、长轮询优化
  • 混合模式:根据消息特点选择不同策略

139. 你使用过哪些 Java 并发工具类?

展开 中等 Java并发 Java

java.util.concurrent包中的主要工具类:

1. CountDownLatch(倒计时门栓)

public class CountDownLatchExample {

public void example() throws InterruptedException {
int threadCount = 3;
CountDownLatch latch = new CountDownLatch(threadCount);

// 启动多个线程
for (int i = 0; i < threadCount; i++) {
new Thread(() -> {
try {
// 执行任务
doWork();
} finally {
latch.countDown(); // 计数减1
}
}).start();
}

// 等待所有线程完成
latch.await();
System.out.println("All threads completed");
}

// 应用场景:等待多个服务启动完成
public void waitForServicesStartup() throws InterruptedException {
CountDownLatch startupLatch = new CountDownLatch(3);

startService("DatabaseService", startupLatch);
startService("CacheService", startupLatch);
startService("MessageService", startupLatch);

startupLatch.await(30, TimeUnit.SECONDS); // 最多等待30秒
}
}

2. CyclicBarrier(循环栅栏)

public class CyclicBarrierExample {

public void example() {
int parties = 3;
CyclicBarrier barrier = new CyclicBarrier(parties, () -> {
System.out.println("All threads reached barrier, proceeding...");
});

for (int i = 0; i < parties; i++) {
new Thread(() -> {
try {
// 第一阶段工作
doPhase1();
barrier.await(); // 等待其他线程完成第一阶段

// 第二阶段工作
doPhase2();
barrier.await(); // 等待其他线程完成第二阶段

} catch (Exception e) {
e.printStackTrace();
}
}).start();
}
}
}

3. Semaphore(信号量)

public class SemaphoreExample {

// 限制同时访问资源的线程数
private final Semaphore semaphore = new Semaphore(3); // 允许3个并发

public void accessResource() {
try {
semaphore.acquire(); // 获取许可
try {
// 访问受限资源
System.out.println("Accessing resource: " + Thread.currentThread().getName());
Thread.sleep(2000);
} finally {
semaphore.release(); // 释放许可
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}

// 数据库连接池示例
public class DatabaseConnectionPool {
private final Semaphore connections;
private final Queue<Connection> pool;

public DatabaseConnectionPool(int maxConnections) {
this.connections = new Semaphore(maxConnections);
this.pool = new ConcurrentLinkedQueue<>();
// 初始化连接池
}

public Connection getConnection() throws InterruptedException {
connections.acquire();
Connection conn = pool.poll();
return conn != null ? conn : createNewConnection();
}

public void returnConnection(Connection conn) {
pool.offer(conn);
connections.release();
}
}
}

4. Exchanger(交换器)

public class ExchangerExample {

public void example() {
Exchanger<String> exchanger = new Exchanger<>();

// 生产者线程
new Thread(() -> {
try {
String data = "Producer Data";
String received = exchanger.exchange(data);
System.out.println("Producer received: " + received);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}).start();

// 消费者线程
new Thread(() -> {
try {
String data = "Consumer Data";
String received = exchanger.exchange(data);
System.out.println("Consumer received: " + received);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}).start();
}
}

5. Phaser(分阶段器)

public class PhaserExample {

public void example() {
Phaser phaser = new Phaser(1); // 主线程注册

for (int i = 0; i < 3; i++) {
phaser.register(); // 注册参与者
new Thread(() -> {
try {
// 阶段1
doPhase1();
phaser.arriveAndAwaitAdvance();

// 阶段2
doPhase2();
phaser.arriveAndAwaitAdvance();

// 阶段3
doPhase3();
phaser.arriveAndDeregister(); // 完成并注销

} catch (Exception e) {
e.printStackTrace();
}
}).start();
}

phaser.arriveAndDeregister(); // 主线程注销
}
}

6. CompletableFuture(可完成的Future)

public class CompletableFutureExample {

public void asyncComposition() {
CompletableFuture<String> future1 = CompletableFuture.supplyAsync(() -> {
return "Hello";
});

CompletableFuture<String> future2 = CompletableFuture.supplyAsync(() -> {
return "World";
});

// 组合多个异步任务
CompletableFuture<String> combined = future1.thenCombine(future2, (s1, s2) -> {
return s1 + " " + s2;
});

combined.thenAccept(System.out::println);

// 异步链式调用
CompletableFuture.supplyAsync(() -> "http://example.com")
.thenCompose(this::fetchData)
.thenApply(this::parseData)
.thenAccept(this::saveData)
.exceptionally(throwable -> {
log.error("Error in async chain", throwable);
return null;
});
}

// 并行执行多个任务
public void parallelExecution() {
List<CompletableFuture<String>> futures = Arrays.asList(
CompletableFuture.supplyAsync(() -> fetchFromService1()),
CompletableFuture.supplyAsync(() -> fetchFromService2()),
CompletableFuture.supplyAsync(() -> fetchFromService3())
);

CompletableFuture<Void> allOf = CompletableFuture.allOf(
futures.toArray(new CompletableFuture[0])
);

CompletableFuture<List<String>> results = allOf.thenApply(v ->
futures.stream()
.map(CompletableFuture::join)
.collect(Collectors.toList())
);
}
}

7. 原子类(Atomic Classes)

public class AtomicExample {

private final AtomicInteger counter = new AtomicInteger(0);
private final AtomicReference<String> atomicString = new AtomicReference<>("initial");
private final AtomicBoolean flag = new AtomicBoolean(false);

// 线程安全的计数器
public void incrementCounter() {
counter.incrementAndGet();
}

// CAS操作
public boolean updateString(String expected, String newValue) {
return atomicString.compareAndSet(expected, newValue);
}

// 原子性更新对象
private final AtomicReference<User> atomicUser = new AtomicReference<>();

public void updateUser(Function<User, User> updateFunction) {
User current, updated;
do {
current = atomicUser.get();
updated = updateFunction.apply(current);
} while (!atomicUser.compareAndSet(current, updated));
}
}

8. 并发集合类

public class ConcurrentCollectionExample {

// 线程安全的Map
private final ConcurrentHashMap<String, String> cache = new ConcurrentHashMap<>();

// 线程安全的队列
private final BlockingQueue<Task> taskQueue = new LinkedBlockingQueue<>();
private final BlockingQueue<Task> priorityQueue = new PriorityBlockingQueue<>();

// 生产者-消费者模式
public void producerConsumerExample() {
// 生产者
new Thread(() -> {
try {
for (int i = 0; i < 10; i++) {
taskQueue.put(new Task("Task " + i));
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}).start();

// 消费者
new Thread(() -> {
try {
while (true) {
Task task = taskQueue.take(); // 阻塞等待
processTask(task);
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}).start();
}

// 并发安全的Set
private final Set<String> concurrentSet = ConcurrentHashMap.newKeySet();

// 写时复制的List
private final List<String> copyOnWriteList = new CopyOnWriteArrayList<>();
}

9. 锁相关类

public class LockExample {

private final ReentrantLock lock = new ReentrantLock();
private final ReadWriteLock readWriteLock = new ReentrantReadWriteLock();
private final StampedLock stampedLock = new StampedLock();

// 可重入锁
public void reentrantLockExample() {
lock.lock();
try {
// 临界区代码
nestedMethod(); // 可重入
} finally {
lock.unlock();
}
}

private void nestedMethod() {
lock.lock(); // 同一线程可再次获取锁
try {
// 嵌套调用
} finally {
lock.unlock();
}
}

// 读写锁
private String data;

public String readData() {
readWriteLock.readLock().lock();
try {
return data;
} finally {
readWriteLock.readLock().unlock();
}
}

public void writeData(String newData) {
readWriteLock.writeLock().lock();
try {
this.data = newData;
} finally {
readWriteLock.writeLock().unlock();
}
}

// StampedLock - Java 8+
public String optimisticRead() {
long stamp = stampedLock.tryOptimisticRead();
String currentData = data;

if (!stampedLock.validate(stamp)) {
// 乐观读失败,降级为悲观读
stamp = stampedLock.readLock();
try {
currentData = data;
} finally {
stampedLock.unlockRead(stamp);
}
}
return currentData;
}
}

10. Fork/Join框架

public class ForkJoinExample extends RecursiveTask<Long> {

private final long[] array;
private final int start;
private final int end;
private static final int THRESHOLD = 1000;

public ForkJoinExample(long[] array, int start, int end) {
this.array = array;
this.start = start;
this.end = end;
}

@Override
protected Long compute() {
if (end - start <= THRESHOLD) {
// 直接计算
long sum = 0;
for (int i = start; i < end; i++) {
sum += array[i];
}
return sum;
} else {
// 分治
int mid = start + (end - start) / 2;
ForkJoinExample leftTask = new ForkJoinExample(array, start, mid);
ForkJoinExample rightTask = new ForkJoinExample(array, mid, end);

leftTask.fork(); // 异步执行左任务
long rightResult = rightTask.compute(); // 同步执行右任务
long leftResult = leftTask.join(); // 等待左任务完成

return leftResult + rightResult;
}
}

public static void main(String[] args) {
long[] array = new long[10000];
// 初始化数组...

ForkJoinPool pool = new ForkJoinPool();
ForkJoinExample task = new ForkJoinExample(array, 0, array.length);
long result = pool.invoke(task);

System.out.println("Sum: " + result);
}
}

实际应用场景总结:

工具类适用场景典型应用
CountDownLatch等待多个任务完成系统启动、批量处理
CyclicBarrier多阶段任务同步并行计算、分阶段处理
Semaphore资源访问控制连接池、限流
Exchanger线程间数据交换生产者消费者
CompletableFuture异步编程微服务调用、并行处理
原子类无锁并发计数器、状态标记
并发集合线程安全容器缓存、队列
各种锁同步控制临界区保护

140. 什么是设计模式?请简述其作用。

展开 简单 VIP 设计模式

设计模式定义: 设计模式是在软件设计中针对特定问题的成熟解决方案,它描述了在特定情况下如何解决设计问题的可重用模板。

设计模式的作用:

1. 提高代码复用性

  • 提供通用的解决方案模板
  • 减少重复代码编写
  • 形成可复用的设计经验

2. 增强代码可维护性

  • 使代码结构更清晰
  • 降低模块间耦合度
  • 便于后期修改和扩展

3. 促进团队协作

  • 提供统一的设计语言
  • 便于沟通和理解
  • 减少设计分歧

4. 提升设计质量

  • 避免常见设计错误
  • 遵循设计原则
  • 提高系统的健壮性

三大类设计模式:

创建型模式(Creational Patterns): 解决对象创建问题

1. 单例模式(Singleton)

public class DatabaseConnection {
private static volatile DatabaseConnection instance;
private static final Object lock = new Object();

private DatabaseConnection() {
// 私有构造函数
}

public static DatabaseConnection getInstance() {
if (instance == null) {
synchronized (lock) {
if (instance == null) {
instance = new DatabaseConnection();
}
}
}
return instance;
}
}

2. 工厂模式(Factory)

public abstract class PaymentFactory {
public static Payment createPayment(String type) {
switch (type.toLowerCase()) {
case "alipay":
return new AlipayPayment();
case "wechat":
return new WechatPayment();
case "credit":
return new CreditCardPayment();
default:
throw new IllegalArgumentException("Unknown payment type");
}
}
}

3. 建造者模式(Builder)

public class HttpRequest {
private final String url;
private final String method;
private final Map<String, String> headers;
private final String body;

private HttpRequest(Builder builder) {
this.url = builder.url;
this.method = builder.method;
this.headers = builder.headers;
this.body = builder.body;
}

public static class Builder {
private String url;
private String method = "GET";
private Map<String, String> headers = new HashMap<>();
private String body;

public Builder url(String url) {
this.url = url;
return this;
}

public Builder method(String method) {
this.method = method;
return this;
}

public Builder header(String key, String value) {
this.headers.put(key, value);
return this;
}

public Builder body(String body) {
this.body = body;
return this;
}

public HttpRequest build() {
return new HttpRequest(this);
}
}
}

// 使用示例
HttpRequest request = new HttpRequest.Builder()
.url("https://api.example.com/users")
.method("POST")
.header("Content-Type", "application/json")
.body("{\"name\":\"John\"}")
.build();

结构型模式(Structural Patterns): 解决类和对象组合问题

1. 适配器模式(Adapter)

// 旧接口
interface OldPrinter {
void printOld(String text);
}

// 新接口
interface NewPrinter {
void print(String text, boolean color);
}

// 适配器
public class PrinterAdapter implements NewPrinter {
private OldPrinter oldPrinter;

public PrinterAdapter(OldPrinter oldPrinter) {
this.oldPrinter = oldPrinter;
}

@Override
public void print(String text, boolean color) {
if (color) {
oldPrinter.printOld("[COLOR]" + text);
} else {
oldPrinter.printOld(text);
}
}
}

2. 装饰器模式(Decorator)

// 基础组件
public interface Coffee {
double cost();
String description();
}

public class SimpleCoffee implements Coffee {
@Override
public double cost() {
return 2.0;
}

@Override
public String description() {
return "Simple Coffee";
}
}

// 装饰器基类
public abstract class CoffeeDecorator implements Coffee {
protected Coffee coffee;

public CoffeeDecorator(Coffee coffee) {
this.coffee = coffee;
}
}

// 具体装饰器
public class MilkDecorator extends CoffeeDecorator {
public MilkDecorator(Coffee coffee) {
super(coffee);
}

@Override
public double cost() {
return coffee.cost() + 0.5;
}

@Override
public String description() {
return coffee.description() + ", Milk";
}
}

// 使用示例
Coffee coffee = new SimpleCoffee();
coffee = new MilkDecorator(coffee);
coffee = new SugarDecorator(coffee);

行为型模式(Behavioral Patterns): 解决对象间通信和职责分配问题

1. 观察者模式(Observer)

public interface Observer {
void update(String news);
}

public class NewsAgency {
private List<Observer> observers = new ArrayList<>();
private String news;

public void addObserver(Observer observer) {
observers.add(observer);
}

public void removeObserver(Observer observer) {
observers.remove(observer);
}

public void setNews(String news) {
this.news = news;
notifyObservers();
}

private void notifyObservers() {
for (Observer observer : observers) {
observer.update(news);
}
}
}

public class NewsChannel implements Observer {
private String name;

public NewsChannel(String name) {
this.name = name;
}

@Override
public void update(String news) {
System.out.println(name + " received news: " + news);
}
}

2. 策略模式(Strategy)

public interface PaymentStrategy {
void pay(double amount);
}

public class CreditCardPayment implements PaymentStrategy {
private String cardNumber;

public CreditCardPayment(String cardNumber) {
this.cardNumber = cardNumber;
}

@Override
public void pay(double amount) {
System.out.println("Paid $" + amount + " using Credit Card " + cardNumber);
}
}

public class PayPalPayment implements PaymentStrategy {
private String email;

public PayPalPayment(String email) {
this.email = email;
}

@Override
public void pay(double amount) {
System.out.println("Paid $" + amount + " using PayPal " + email);
}
}

public class ShoppingCart {
private PaymentStrategy paymentStrategy;

public void setPaymentStrategy(PaymentStrategy paymentStrategy) {
this.paymentStrategy = paymentStrategy;
}

public void checkout(double amount) {
paymentStrategy.pay(amount);
}
}

设计原则:

SOLID原则:

  • S - 单一职责原则
  • O - 开闭原则
  • L - 里氏替换原则
  • I - 接口隔离原则
  • D - 依赖倒置原则

实际应用示例:

// Spring框架中的设计模式应用
@Service
public class UserService {

// 依赖注入(控制反转)
@Autowired
private UserRepository userRepository;

// 模板方法模式
@Transactional
public void updateUser(User user) {
// 框架处理事务开始
userRepository.save(user);
// 框架处理事务提交/回滚
}

// 代理模式(AOP)
@Cacheable("users")
public User getUser(Long id) {
return userRepository.findById(id);
}
}

学习建议:

  1. 理解问题场景和解决思路
  2. 掌握常用模式的实现
  3. 在实际项目中应用
  4. 避免过度设计
  5. 结合具体框架理解应用