java面试场景问题
还在补充,这几天工作忙,闲了会把答案附上去,也欢迎各位大佬评论区讨论
1.不用分布式锁如何防重复提交
方法 1:基于唯一请求 ID(幂等 Token)
思路:前端生成 一个唯一的 requestId(如 UUID或者能表示本次请求的唯一标识字段),每次提交请求时带上它。
后端使用 Redis存储 requestId,如果已经存在,则拒绝处理。
实现:前端:每次提交请求时,生成一个 requestId:
javascript
const requestId = crypto.randomUUID(); // 生成唯一 ID
axios.post('/api/submit', { data, requestId });
后端java
@Autowired
private StringRedisTemplate redisTemplate;public ResponseEntity<String> submitRequest(String requestId, Data data) {Boolean isDuplicate = redisTemplate.opsForValue().setIfAbsent("request:" + requestId, "1", 10, TimeUnit.MINUTES);if (Boolean.FALSE.equals(isDuplicate)) {return ResponseEntity.status(HttpStatus.CONFLICT).body("重复提交");}try {// 处理业务逻辑process(data);return ResponseEntity.ok("提交成功");} finally {redisTemplate.delete("request:" + requestId); // 可选:若确保幂等性,可不删除}
}
优点: ✅ 适用于分布式系统
✅ 性能好(基于 Redis 操作)
✅ Token 过期时间 防止长期占用
方法 2:数据库唯一索引
思路:让数据库的唯一索引防止重复提交,常用于订单号、业务唯一键。
实现
数据库表添加唯一索引:
sql
ALTER TABLE orders ADD UNIQUE (order_no);
后端插入数据:
try {orderMapper.insert(order);
} catch (DuplicateKeyException e) {return ResponseEntity.status(HttpStatus.CONFLICT).body("重复提交");
}
优点: ✅ 数据库级防重,最可靠
✅ 适合订单、支付等业务场景
⛔ 性能受限于数据库,高并发需优化
方法 3:前端按钮防抖
思路:提交后禁用按钮,直到返回响应,防止用户快速点击。
实现
document.getElementById("submit-btn").addEventListener("click", function() {this.disabled = true; // 禁用按钮axios.post('/api/submit', { data }).then(response => alert(response.data)).finally(() => this.disabled = false); // 请求完成后恢复
});
优点: ✅ 简单易行,无需改后端
⛔ 前端可绕过,不适用于高安全性场景
方法 4:悲观锁(数据库行锁)
思路:通过 SELECT … FOR UPDATE 加行锁,确保事务内数据不会被其他请求修改。
实现
@Transactional
public void submitOrder(Long orderId) {Order order = orderMapper.selectByIdForUpdate(orderId); // 加锁if (order.getStatus() != OrderStatus.PENDING) {throw new IllegalStateException("订单已处理");}order.setStatus(OrderStatus.PROCESSED);orderMapper.updateById(order);
}
优点: ✅ 确保单线程执行,避免重复
⛔ 数据库性能受影响,不适用于高并发
总结

)
推荐方案:
高并发系统:幂等 Token + Redis ✅
数据库事务业务:唯一索引 / 状态机 ✅
简单防重:前端按钮防抖 ✅
如果并发压力大,可以结合多种方案,例如:
Redis 幂等 Token + 数据库唯一索引 🚀
前端防抖 + 后端幂等 Token 🔥
2.redis的哨兵模式是如何选举的
Redis 的哨兵模式(Sentinel)用于监控 Redis 服务器,并在主服务器(Master)宕机时自动执行故障转移(Failover)。哨兵模式的选举过程主要发生在主服务器不可用时,选举一个新的主服务器。以下是选举的具体步骤:
- 发现主服务器故障
每个哨兵(Sentinel)会定期向主服务器和从服务器发送 PING 命令,检查它们的状态。
如果多个哨兵在 down-after-milliseconds 时间内没有收到主服务器的响应,就会认为它 主观下线(Subjectively Down, sDown)。
当 大多数 哨兵都认定主服务器宕机时,主服务器就会进入 客观下线(Objectively Down, oDown) 状态,触发故障转移。 - 哨兵选举领导者
当主服务器 oDown 后,需要一个哨兵来执行故障转移,因此哨兵之间需要进行 Raft 算法 类似的选举流程:
所有哨兵都可以参与选举,尝试成为领导者(Leader)。
每个哨兵向其他哨兵发送 SENTINEL is-master-down-by-addr 请求,询问它们是否同意自己成为领导者。
其他哨兵如果还没有投票,则会同意投票给该请求的哨兵。
如果一个哨兵获得超过一半的票数(多数派),就会成为领导者。
3. 选择新的主服务器
领导者哨兵会从现有的从服务器中选择一个最合适的来提升为新的主服务器:
选择 复制进度最接近主服务器 的从服务器(偏移量最大)。
如果多个从服务器复制进度相同,选择 ID 最小 的。
如果没有合适的从服务器,则失败,等待下一轮选举。
4. 执行故障转移
领导者哨兵发送 slaveof no one 命令,让选中的从服务器成为新的主服务器。
让其他从服务器执行 slaveof <new_master>,将它们的主服务器指向新的主服务器。
更新配置信息,广播新的主服务器地址给所有 Redis 客户端。
5. 恢复监控
整个集群稳定后,哨兵继续监控新的主服务器和从服务器,准备处理下一次故障。
通过这种选举机制,Redis 的哨兵模式能够自动检测主服务器故障,并确保集群能够继续运行。
3.线程池的原理
-
线程池的概念
线程池(Thread Pool)是一种用于管理和复用线程的技术,它维护了一组可复用的线程,避免了频繁创建和销毁线程的开销,从而提高程序的执行效率。 -
线程池的核心组成
线程池主要由以下几个核心部分组成:
线程队列(BlockingQueue)
用于存放等待执行的任务。常见的队列类型:
无界队列(LinkedBlockingQueue):适用于任务量大但不会超出系统资源的情况。
有界队列(ArrayBlockingQueue):适用于控制任务数量,防止资源耗尽。
优先队列(PriorityBlockingQueue):任务可根据优先级执行。
核心线程数(Core Pool Size)
线程池初始化后,线程数不超过核心线程数时,即使空闲,也不会销毁。
最大线程数(Maximum Pool Size)
当任务数量超过核心线程数时,线程池可以临时创建额外的线程,但不会超过最大线程数。
任务拒绝策略(Rejection Policy)
当线程池达到最大线程数且任务队列已满时,新任务将被拒绝。常见策略:
AbortPolicy(默认):抛出异常。
CallerRunsPolicy:调用线程自己执行任务,降低任务提交速率。
DiscardPolicy:直接丢弃任务,不处理也不抛出异常。
DiscardOldestPolicy:丢弃队列中最早的任务,再尝试执行新任务。
线程工厂(ThreadFactory)
用于创建线程,可以自定义线程命名、设置守护线程等。
存活时间(Keep Alive Time)
当线程数超过核心线程数时,多余的空闲线程会在超过该时间后被销毁。
3. 线程池的工作流程
任务提交到线程池。
如果当前运行线程数小于核心线程数,直接创建新线程执行任务。
如果核心线程数已满,则任务进入队列等待。
若队列已满且线程数小于最大线程数,则创建新线程执行任务。
若线程数达到最大值且任务队列也满了,则触发任务拒绝策略。
线程执行完任务后,若线程数超过核心线程数,多余的空闲线程会在 keepAliveTime 超过后被销毁。
4. 线程池的优点
提高性能:减少线程创建和销毁的开销。
提高资源利用率:合理分配线程,避免资源浪费。
控制并发:防止创建过多线程导致系统资源耗尽。
5. 线程池的应用
在 Java 中,ExecutorService 提供了常见的线程池实现:
ExecutorService executor = Executors.newFixedThreadPool(5);
executor.submit(() -> {System.out.println("任务执行:" + Thread.currentThread().getName());
});
executor.shutdown();
- 自定义线程池
使用 ThreadPoolExecutor 自定义线程池:
ExecutorService threadPool = new ThreadPoolExecutor(2, // 核心线程数5, // 最大线程数60, // 线程空闲存活时间TimeUnit.SECONDS, // 时间单位new LinkedBlockingQueue<>(10), // 任务队列Executors.defaultThreadFactory(), // 线程工厂new ThreadPoolExecutor.AbortPolicy() // 拒绝策略
);
4.spring的spi和dubbo的spi机制是什么?
Spring 的 SPI(Service Provider Interface) 主要基于 Java 原生的 java.util.ServiceLoader 机制,并在此基础上进行了增强。它主要用于加载和扩展 Spring 组件,如 SpringFactoriesLoader。
1. Java SPI 机制
Java 提供 java.util.ServiceLoader,用于加载 META-INF/services/ 目录下的服务配置文件。
这个配置文件的命名规则是接口的全限定名,内容是具体的实现类。
示例
定义 SPI 接口
public interface MyService {void execute();
}//提供实现
public class MyServiceImpl implements MyService {@Overridepublic void execute() {System.out.println("MyServiceImpl executed");}
}
在 META-INF/services/ 目录下,创建文件 com.example.MyService,内容如下:
com.example.MyServiceImpl
使用 ServiceLoader 进行加载:
ServiceLoader<MyService> loader = ServiceLoader.load(MyService.class);
for (MyService service : loader) {service.execute();
}
2. Spring 的 SPI
Spring 在 Java SPI 机制基础上,增加了 SpringFactoriesLoader,用于从 META-INF/spring.factories 文件中加载扩展组件。
Spring SPI 加载方式:
主要通过 org.springframework.core.io.support.SpringFactoriesLoader 进行加载。
读取 META-INF/spring.factories 配置文件,该文件的内容格式为:
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.example.MyAutoConfiguration
在 Spring Boot 自动装配(AutoConfiguration)中,大量使用了该机制。
示例
1.在 spring.factories 文件中配置:
org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.example.MyAutoConfiguration
2.MyAutoConfiguration 类:
@Configuration
public class MyAutoConfiguration {@Beanpublic MyService myService() {return new MyServiceImpl();}
}
3.Spring Boot 在启动时,会通过 SpringFactoriesLoader.loadFactoryNames(EnableAutoConfiguration.class, classLoader) 自动加载 MyAutoConfiguration 类,从而完成自动配置。
3.Dubbo 的 SPI 机制
Dubbo 也使用了 SPI 机制,但相比 Java SPI 进行了增强,主要特点包括:
自定义 SPI 机制:Dubbo 提供了 ExtensionLoader,替代 Java 的 ServiceLoader,支持 AOP、IoC 和自适应扩展等功能。
META-INF/dubbo/ 目录:Dubbo 的 SPI 机制约定配置文件放在 META-INF/dubbo/ 或 META-INF/services/ 目录下。
@SPI 注解:Dubbo 允许在接口上使用 @SPI 进行标注,指明默认实现。
支持 @Adaptive 注解:Dubbo 支持自适应扩展,能够根据参数动态选择实现类。
- Dubbo SPI 使用方式
1>定义 SPI 接口
import org.apache.dubbo.common.extension.SPI;@SPI("defaultImpl") // 指定默认实现
public interface MyService {void execute();
}
2>提供多个实现
public class DefaultImpl implements MyService {@Overridepublic void execute() {System.out.println("Executing Default Implementation");}
}public class AdvancedImpl implements MyService {@Overridepublic void execute() {System.out.println("Executing Advanced Implementation");}
}
3>配置 META-INF/dubbo/com.example.MyService
defaultImpl=com.example.DefaultImpl
advancedImpl=com.example.AdvancedImpl
4>通过 Dubbo 的 ExtensionLoader 加载
ExtensionLoader<MyService> loader = ExtensionLoader.getExtensionLoader(MyService.class);
MyService service = loader.getDefaultExtension(); // 获取默认实现
service.execute();MyService advancedService = loader.getExtension("advancedImpl"); // 获取指定实现
advancedService.execute();
- Dubbo SPI 的增强点
默认实现:@SPI(“defaultImpl”) 指定默认实现。
自动注入:Dubbo 的扩展点支持 IoC,可以自动注入依赖。
自适应扩展:@Adaptive 允许根据运行时参数动态选择实现。
Wrapper 扩展:支持 AOP 方式的扩展,如日志增强。
总结

Spring SPI 主要用于 Spring Boot 自动装配。
Dubbo SPI 主要用于服务扩展,提供了更强的动态适配能力。
Dubbo SPI 机制在性能、可扩展性和动态适应方面,比 Spring 和 Java SPI 机制更加强大。
5.流量激增,大批量数据如何处理?
- 数据分片与分区
数据库分片(Sharding):将数据拆分到多个数据库实例上,减少单个数据库的压力。
分区表(Partitioning):对大表进行分区存储,提高查询性能,例如按时间、地域等划分。 - 高效的缓存策略
CDN(内容分发网络):对于静态资源(图片、视频、文件等),使用CDN分发可减少源站压力。
Redis/Memcached:对于热点数据,将查询结果缓存,减少数据库访问次数。
本地缓存:在应用层使用LRU缓存避免频繁访问远程存储。 - 异步处理与消息队列
消息队列(Kafka、RabbitMQ、RocketMQ):削峰填谷,将高并发请求转换为异步任务,提高吞吐量。
任务队列(Celery、Sidekiq):对于非实时数据处理(如日志分析、统计计算),可以异步执行。 - 扩展架构
水平扩展(Scale Out):增加服务器数量,通过负载均衡(Nginx、HAProxy)分发流量。
垂直扩展(Scale Up):提升单机性能,如升级CPU、内存、磁盘IO能力。 - 数据流处理
流式计算(Flink、Spark Streaming、Storm):用于实时数据处理,避免批量计算的延迟问题。
批处理(Hadoop、Spark):适用于大规模离线分析任务。 - 数据库优化
索引优化:合理使用B+树索引、哈希索引等加速查询。
SQL优化:避免N+1查询,使用JOIN优化查询结构。
读写分离:主从数据库架构,分离读写操作,提高查询性能。 - 日志及监控
ELK(Elasticsearch + Logstash + Kibana):用于日志分析,监测异常流量。
Prometheus + Grafana:监控系统状态,及时发现瓶颈。
通过以上策略,可以高效应对流量激增和大批量数据处理,提高系统稳定性和响应速度。
6.rabbitmq如何防止重复消费,高并发情况下
- 确保手动 ACK
避免使用自动 ACK,确保消息处理完毕后再确认
✅ 正确做法(手动 ACK)
channel.basicConsume(QUEUE_NAME, false, new DefaultConsumer(channel) {@Overridepublic void handleDelivery(String consumerTag, Envelope envelope,AMQP.BasicProperties properties, byte[] body) throws IOException {try {String message = new String(body, "UTF-8");processMessage(message); // 处理消息// 处理完成后手动确认channel.basicAck(envelope.getDeliveryTag(), false);} catch (Exception e) {// 处理失败,拒绝消息并重新放回队列channel.basicNack(envelope.getDeliveryTag(), false, true);}}
});
📌 说明
autoAck=false (手动 ACK)
basicAck() (成功后确认消息已处理)
basicNack() (失败后重新入队)
- 业务层去重
即使 RabbitMQ 保障**“至少一次”(At Least Once)**投递,仍可能发生重复消费,因此需要在业务层去重。
✅ 方法 1:数据库唯一约束
可以使用数据库的唯一索引字段,如订单号 order_id:
INSERT INTO orders (order_id, user_id, amount)
VALUES ('msg_123456', 'user_001', 100)
ON DUPLICATE KEY UPDATE order_id=order_id;
如果 order_id 已存在,则不会插入新的记录。
✅ 方法 2:Redis 去重
使用 Redis SETNX(防止短时间内重复处理)
public boolean isProcessed(String messageId) {String key = "msg_" + messageId;Boolean success = redisTemplate.opsForValue().setIfAbsent(key, "1", 60, TimeUnit.SECONDS);return success != null && !success;
}public void processMessage(String message) {if (isProcessed(message)) {System.out.println("消息已处理,跳过: " + message);return;}// 业务逻辑处理
}
- 设置消息 TTL + 死信队列(DLX)
防止重复消费时死循环
✅ 设置消息 TTL
Map<String, Object> args = new HashMap<>();
args.put("x-message-ttl", 60000); // 60s 后消息过期
args.put("x-dead-letter-exchange", "dlx_exchange"); // 指定死信交换机
channel.queueDeclare("task_queue", true, false, false, args);
✅ 死信队列(DLX)
当消息失败多次后,可以转移到死信队列,避免重复消费:
args.put("x-dead-letter-routing-key", "dlx_routing_key");
- 限制消费者并发数
如果消费者并发过高,可能会导致 RabbitMQ 消息重复投递,可以限制每个消费者最多只能预取 1 条消息:
channel.basicQos(1); // 每个消费者一次只处理 1 条消息
- 生产者端消息确认
RabbitMQ 提供 Confirm 机制,确保消息不会丢失
channel.confirmSelect(); // 开启发布确认模式channel.basicPublish("", "task_queue", MessageProperties.PERSISTENT_TEXT_PLAIN, message.getBytes("UTF-8"));// 等待确认
if (!channel.waitForConfirms()) {System.out.println("消息发送失败");
}
总结
✅ 手动 ACK 机制(避免自动 ACK,确保消息处理完毕后才确认)
✅ 业务去重(数据库唯一索引,Redis SETNX)
✅ TTL + 死信队列(防止重复消费导致的死循环)
✅ 限制并发数(basicQos(1) 限制每个消费者最多消费 1 条)
✅ 发布确认(waitForConfirms() 确保消息可靠投递)
使用这些优化方法,可以有效防止 RabbitMQ 在 高并发情况下的重复消费,保障消息一致性和系统稳定性!🚀
7消息堆积如何处理?
在 RabbitMQ 高并发环境下,如果消息处理速度赶不上生产速度,容易导致 消息堆积,最终可能引发 队列溢出、服务器崩溃 等问题。这里介绍 消息堆积的原因分析 以及 有效的解决方案。
✅ 解决方案
- 增加消费者并发数
方法:
增加 消费者实例(多个 Worker 并发消费)
调整 预取数 prefetch_count,避免一个消费者一次性接收过多消息
📌 示例(Java 代码:增加并发消费者)
channel.basicQos(10); // 每个消费者最多处理 10 条消息
这样可以让消费者更快地处理消息,减少队列堆积。
- 使用消息分片 & 负载均衡
当单个队列压力过大时,可以通过 多队列 + 负载均衡 分担流量:
方法 1:按 消息特性 分片(如订单队列、支付队列、库存队列)
方法 2:多个消费者监听 不同的队列 处理同一类任务
方法 3:使用 交换机(Exchange) 进行路由
📌 示例(不同消费者监听不同的队列)
channel.queueDeclare("order_queue", true, false, false, null);
channel.queueDeclare("payment_queue", true, false, false, null);
优点:避免某一个队列因负载过大而导致 RabbitMQ 挂掉。
- 启用 RabbitMQ 集群
如果 RabbitMQ 单节点性能不足,可以使用 RabbitMQ 集群,提高系统的吞吐能力:
镜像队列模式:保证高可用
分布式队列模式:多个 RabbitMQ 节点共同处理消息
📌 示例(启用集群)
rabbitmqctl join_cluster rabbit@node2
优点:多个 RabbitMQ 服务器 分担流量,避免单点压力过大。
- 配置消息 TTL + 死信队列
如果消息长时间堆积,可以设置 消息过期时间(TTL),并把未处理的消息转入 死信队列(DLX)。
📌 示例(配置 TTL + DLX)
Map<String, Object> args = new HashMap<>();
args.put("x-message-ttl", 60000); // 消息存活 60s
args.put("x-dead-letter-exchange", "dlx_exchange"); // 过期消息转入死信队列
channel.queueDeclare("task_queue", true, false, false, args);
优点:
- 限制消息存活时间,防止无用消息无限堆积。
- 过期消息进入 死信队列,避免影响正常业务。
- 限流(限流+降级)
如果生产者产生消息过快,可以限制生产速度,或直接丢弃部分非核心消息。
限流方式:
- RabbitMQ basic.qos 控制流速
- Kafka 或 Redis 作为缓冲层
- 应用层限流(如令牌桶、漏桶算法)
📌 示例(使用 basic.qos 进行流量控制)
channel.basicQos(5); // 限制每个消费者每次最多消费 5 条
优点:可以防止 RabbitMQ 被瞬时高并发流量压垮。
- 监控 & 自动扩展
- 使用 RabbitMQ 监控工具(Prometheus + Grafana / RabbitMQ Management)
- 设置自动扩容机制(Kubernetes HPA)
📌 示例(监控 RabbitMQ 队列长度)
rabbitmqctl list_queues name messages_ready messages_unacknowledged
如果队列长度超过阈值,可以 动态增加消费者,提高处理能力。
🔥 总结

如果你的 RabbitMQ 消息已经堆积过多,可以:
- 临时增加消费者实例(短期缓解)
- 快速清理过期/无用消息(避免资源占满)
- 优化消息分片 & 负载均衡(长期解决)
8.用过那些监控ivm的工具?
IVM(Intelligent Virtual Machine,智能虚拟机)监控 需要关注 CPU、内存、磁盘、网络 以及 应用服务 的健康状态。常见的 IVM 监控工具有:

✅ 如何实现 IVM 监控
- 使用 Prometheus + Grafana 监控 IVM
架构:
- Node Exporter(采集虚拟机 CPU、内存、磁盘、网络)
- Prometheus(存储 & 报警)
- Grafana(可视化)
📌 步骤 1️⃣ 安装 Node Exporter(虚拟机监控)
wget https://github.com/prometheus/node_exporter/releases/latest/download/node_exporter-linux-amd64.tar.gz
tar -xzf node_exporter-linux-amd64.tar.gz
cd node_exporter-*
./node_exporter &
2️⃣ 配置 Prometheus 采集虚拟机数据 编辑 prometheus.yml:
yaml
scrape_configs:- job_name: 'ivm-monitor'static_configs:- targets: ['192.168.1.100:9100'] # 监控 IVM 的 IP
3️⃣ 运行 Prometheus
./prometheus --config.file=prometheus.yml
4️⃣ 配置 Grafana 可视化
- 在 Grafana 添加数据源(Prometheus)
- 使用 Node Exporter 监控仪表盘展示 CPU、内存、磁盘使用率
- 使用 Zabbix 监控 IVM
1️⃣ 安装 Zabbix Agent 到 IVM
sudo apt install zabbix-agent
sudo systemctl start zabbix-agent
sudo systemctl enable zabbix-agent
2️⃣ 配置 Zabbix Server 在 /etc/zabbix/zabbix_agentd.conf 添加:
Server=192.168.1.200 # Zabbix 服务器 IP
3️⃣ 在 Zabbix Web UI 中添加 IVM 主机
- 配置 CPU、内存、网络监控
- 设置告警阈值(如 CPU 使用率 > 80% 发送告警)
- 使用 ELK 监控 IVM 日志
1️⃣ 安装 Filebeat 采集日志
sudo apt install filebeat
2️⃣ 配置 Filebeat 发送到 Elasticsearch
output.elasticsearch:hosts: ["192.168.1.200:9200"]
3️⃣ 使用 Kibana 可视化 IVM 日志
🚀 总结
- 大规模 IVM 监控 👉 Prometheus + Grafana
- 企业级稳定监控 👉 Zabbix / Nagios
- 日志监控 👉 ELK
- SaaS 监控 👉 Datadog / New Relic
如果是 高并发分布式 IVM 监控,推荐 Prometheus + Grafana,结合 告警 & 自动扩展,确保系统稳定!💡
9.常用的设计模式有哪些?怎么用的?
设计模式(Design Patterns) 是软件开发中的常见问题解决方案,主要分为 三大类:
- 创建型模式(解决对象创建问题)
- 结构型模式(解决类和对象的组合问题)
- 行为型模式(解决对象交互问题)
🔹 1. 创建型模式
✅ 1.1 单例模式(Singleton)
作用:确保一个类只有一个实例,并提供全局访问点
应用场景:数据库连接池、线程池、日志系统
📌 示例(懒汉式,线程安全)
public class Singleton {private static volatile Singleton instance; // 防止指令重排private Singleton() {} // 私有构造函数,防止外部实例化public static Singleton getInstance() {if (instance == null) {synchronized (Singleton.class) {if (instance == null) {instance = new Singleton();}}}return instance;}
}
✅ 1.2 工厂模式(Factory Pattern)
作用:提供一个创建对象的接口,而不是直接实例化类
应用场景:数据库连接、日志记录器
📌 示例
// 1. 定义接口
interface Product {void create();
}// 2. 具体产品实现
class ConcreteProductA implements Product {public void create() { System.out.println("创建产品 A"); }
}class ConcreteProductB implements Product {public void create() { System.out.println("创建产品 B"); }
}// 3. 工厂类
class Factory {public static Product getProduct(String type) {if ("A".equals(type)) return new ConcreteProductA();else if ("B".equals(type)) return new ConcreteProductB();return null;}
}// 4. 使用工厂
Product product = Factory.getProduct("A");
product.create();
🔹 2. 结构型模式
✅ 2.1 适配器模式(Adapter Pattern)
作用:把一个接口转换成客户端期望的另一个接口
应用场景:兼容老代码、新旧接口对接
📌 示例
// 1. 目标接口
interface Target {void request();
}// 2. 被适配者(旧接口)
class Adaptee {void specificRequest() { System.out.println("调用旧接口"); }
}// 3. 适配器
class Adapter implements Target {private Adaptee adaptee = new Adaptee();public void request() { adaptee.specificRequest(); }
}// 4. 使用适配器
Target adapter = new Adapter();
adapter.request();
✅ 2.2 装饰器模式(Decorator Pattern)
作用:动态扩展类的功能,而不修改原代码
应用场景:日志增强、数据加密、I/O 流
📌 示例
// 1. 定义接口
interface Component {void operation();
}// 2. 具体组件
class ConcreteComponent implements Component {public void operation() { System.out.println("基础功能"); }
}// 3. 装饰器基类
class Decorator implements Component {protected Component component;public Decorator(Component component) { this.component = component; }public void operation() { component.operation(); }
}// 4. 具体装饰器
class ConcreteDecorator extends Decorator {public ConcreteDecorator(Component component) { super(component); }public void operation() {super.operation();System.out.println("扩展功能");}
}// 5. 使用装饰器
Component decorated = new ConcreteDecorator(new ConcreteComponent());
decorated.operation();
🔹 3. 行为型模式
✅ 3.1 观察者模式(Observer Pattern)
作用:一对多依赖,当一个对象状态改变,所有依赖对象自动更新
应用场景:事件监听、消息推送
📌 示例
import java.util.ArrayList;
import java.util.List;// 1. 观察者接口
interface Observer {void update(String message);
}// 2. 具体观察者
class User implements Observer {private String name;public User(String name) { this.name = name; }public void update(String message) {System.out.println(name + " 收到消息: " + message);}
}// 3. 主题(被观察者)
class Subject {private List<Observer> observers = new ArrayList<>();public void addObserver(Observer observer) { observers.add(observer); }public void notifyObservers(String message) {for (Observer observer : observers) observer.update(message);}
}// 4. 测试
Subject subject = new Subject();
Observer user1 = new User("张三");
Observer user2 = new User("李四");
subject.addObserver(user1);
subject.addObserver(user2);
subject.notifyObservers("系统升级");
✅ 3.2 策略模式(Strategy Pattern)
作用:定义一系列算法,让它们可以互换,避免 if-else 代码膨胀
应用场景:支付方式、数据压缩、权限验证
📌 示例
// 1. 策略接口
interface Strategy {void execute();
}// 2. 具体策略
class ConcreteStrategyA implements Strategy {public void execute() { System.out.println("执行策略 A"); }
}class ConcreteStrategyB implements Strategy {public void execute() { System.out.println("执行策略 B"); }
}// 3. 上下文(使用策略)
class Context {private Strategy strategy;public void setStrategy(Strategy strategy) { this.strategy = strategy; }public void executeStrategy() { strategy.execute(); }
}// 4. 测试
Context context = new Context();
context.setStrategy(new ConcreteStrategyA());
context.executeStrategy();
context.setStrategy(new ConcreteStrategyB());
context.executeStrategy();
🔥 总结

10.xxl-job如果任务处理不完怎么办?
🔍 任务堆积的原因分析
- 任务执行时间过长(业务逻辑耗时过多、数据库查询慢)
- 任务并发数太少(单个执行器线程数限制)
- 任务调度间隔过短(调度频率高,任务还未执行完就来了新的)
- 任务失败未重试(导致部分任务一直未完成)
- 执行器负载过高(CPU、内存资源耗尽)
✅ 解决方案
- 增加任务并发数
如果执行器并发数太少,任务处理能力有限,可以通过 线程池 提高并发。
📌 方法 1:配置 XXL-Job 线程池
修改 XXL-Job 执行器配置,提高 xxl.job.executor.logretentiondays
xxl.job.executor.logretentiondays=30
xxl.job.executor.threadpool=20
Java 代码示例(使用线程池)
@XxlJob("parallelJobHandler")
public void parallelJobHandler() throws Exception {ExecutorService executorService = Executors.newFixedThreadPool(10); // 增加线程池for (int i = 0; i < 10; i++) {executorService.submit(() -> {System.out.println("处理任务:" + Thread.currentThread().getName());});}executorService.shutdown();
}
- 增加执行器机器
如果任务量特别大,单个 Executor(执行器) 处理不过来,可以 水平扩展,增加多台机器。
📌 方法:配置 XXL-Job 多个执行器
- 在 Nginx 或负载均衡 中配置多个 XXL-Job 执行器
- 在 xxl-job-admin 配置多个 Executor
- 动态扩容:可以使用 Kubernetes(K8s)+ HPA 自动扩展实例
📌 示例(多个执行器注册)
xxl.job.executor.address=http://192.168.1.100:9999,http://192.168.1.101:9999
- 任务分片(Sharding)
如果任务处理不完,可以 拆分任务,让多个执行器同时处理不同数据片段。
📌 方法:使用 XXL-Job 自带的 Sharding
@XxlJob("shardingJobHandler")
public void shardingJobHandler() throws Exception {int shardIndex = XxlJobHelper.getShardIndex(); // 当前执行器分片索引int shardTotal = XxlJobHelper.getShardTotal(); // 总分片数System.out.println("执行分片:" + shardIndex + "/" + shardTotal);
}
作用:如果有 10000 条数据,可以分 10 片,每个执行器处理 1000 条,提升任务吞吐量。
- 任务超时 & 限流
如果任务执行时间过长,导致后续任务堆积,可以:
设置任务超时时间
限制最大并发数
📌 方法:配置 XXL-Job 超时 & 并发
@XxlJob(value = "timeoutJobHandler", init = "initMethod", destroy = "destroyMethod", timeout = 5000, concurrent = false)
public void timeoutJobHandler() throws Exception {Thread.sleep(6000); // 模拟超时
}
timeout = 5000:超时 5 秒后强制结束任务
concurrent = false:不允许并发执行,防止任务堆积
- 失败重试
如果任务失败,可能会导致数据未处理完。可以开启 失败重试,让 XXL-Job 自动尝试执行。
📌 方法:在 XXL-Job 控制台配置
- 重试次数:默认 3 次
- 失败策略:
-
- 失败重试(RETRY)
-
- 失败报警(FAIL_ALARM)
-
- 丢弃后续任务(DISCARD_LATER)
- 任务排队(消息队列)
如果任务量超大,可以引入 消息队列(MQ) 进行流量削峰。
📌 方法:使用 RabbitMQ / Kafka 进行异步处理
@XxlJob("mqJobHandler")
public void mqJobHandler() throws Exception {String message = "任务数据";rabbitTemplate.convertAndSend("taskQueue", message);
}
让 XXL-Job 只负责写入 MQ,执行器异步消费,防止任务堆积。
🔥 总结

📌 综合优化:
✅ 短期优化 👉 线程池、任务分片、超时处理
✅ 长期优化 👉 负载均衡、多执行器、异步消息队列
如果 XXL-Job 任务一直堆积,建议结合 任务分片 + 多执行器 + MQ 方案,确保高并发场景下任务稳定运行!🚀
11.spring事务失效情况,传播机制,如何保证事务不失效
🔹 1. Spring 事务失效的常见原因
在 Spring 中,事务由 @Transactional 注解管理,但在某些情况下事务可能会失效。常见失效情况包括:

🔹 2. 事务传播机制(Transaction Propagation)
Spring 事务提供 7 种事务传播机制,主要用于嵌套调用时事务的行为控制:

📌 示例
@Service
public class OrderService {@Transactional(propagation = Propagation.REQUIRED)public void createOrder() {userService.addUser(); // 事务加入当前事务paymentService.processPayment(); // 事务加入当前事务}
}
🔹 3. 如何保证事务不失效
为了确保事务能够正常生效,推荐采取以下措施:
✅ 1. @Transactional 方法必须是 public
📌 错误示例(私有方法无效)
@Service
public class MyService {@Transactionalprivate void saveData() { // ❌ 无效// 事务不会生效}
}
📌 正确示例
@Service
public class MyService {@Transactionalpublic void saveData() { // ✅ 必须是 public// 事务正常生效}
}
✅ 2. 确保 @Transactional 方法是由 Spring 托管的 Bean 调用
📌 错误示例(内部调用失效)
@Service
public class MyService {@Transactionalpublic void methodA() {methodB(); // ❌ 内部调用,事务失效}@Transactionalpublic void methodB() {// 事务不会生效}
}
📌 正确示例
@Service
public class MyService {@Transactionalpublic void methodA() {((MyService) AopContext.currentProxy()).methodB(); // ✅ 通过代理调用}@Transactionalpublic void methodB() {// 事务正常生效}
}
✅ 3. 事务异常必须是 RuntimeException
📌 错误示例(捕获异常后事务不回滚)
java
复制
编辑
@Transactional
public void updateData() {
try {
// 业务逻辑
} catch (Exception e) { // ❌ 事务不会回滚
e.printStackTrace();
}
}
📌 正确示例
java
复制
编辑
@Transactional
public void updateData() {
try {
// 业务逻辑
} catch (Exception e) {
TransactionAspectSupport.currentTransactionStatus().setRollbackOnly(); // ✅ 手动回滚
}
}
✅ 4. 多线程事务控制
📌 错误示例(子线程不继承事务)
@Transactional
public void process() {new Thread(() -> saveData()).start(); // ❌ 事务不会生效
}
📌 正确示例
@Transactional
public void process() {CompletableFuture.runAsync(() -> saveData(), Executors.newFixedThreadPool(5)); // ✅ 线程池事务
}
✅ 5. 事务嵌套 & Propagation.REQUIRES_NEW
如果主事务和子事务相互独立,可以使用 REQUIRES_NEW。
📌 示例
@Service
public class OrderService {@Transactional(propagation = Propagation.REQUIRED)public void createOrder() {paymentService.processPayment(); // 事务 A}
}@Service
public class PaymentService {@Transactional(propagation = Propagation.REQUIRES_NEW)public void processPayment() {// 事务 B,独立提交,不受主事务影响}
}
🔥 总结

📌 最佳实践:
- 所有 @Transactional 方法必须是 public
- 确保方法是由 Spring 管理的 Bean 调用(避免内部调用)
- 抛出的异常必须是 RuntimeException,或手动 setRollbackOnly()
- 多线程时要手动绑定事务
- 事务传播机制要合理选择(REQUIRES_NEW / NESTED 等)
12.dubbo和openfeiqn的区别?
13.nacos支持负载均衡吗,策略是什么?
14.了解过逃逸分析吗?
15.hashmap底层结构,什么时间会形成链表,idk1.8前后有啥区别
16.jvm内存结构,新生代的垃圾回收算法,老年代的垃圾回收算法
17.如何创建线程,start方法可不可以调两次,一个线程如何唤起另一个线程。
18.syncornized和Lock有什么区别
19.volatile关键字的实现原理
20.线程池的基本原理,具体参数是什么
21.redis线程模型,redis的主从、哨兵、集群模式区别
22.消息堆积如何处理,消息重复消费如何避免
23.使用过的设计模式
24.mysql的索引原理
25.sql优化方法
26.springcloud用过哪些组件
相关文章:
java面试场景问题
还在补充,这几天工作忙,闲了会把答案附上去,也欢迎各位大佬评论区讨论 1.不用分布式锁如何防重复提交 方法 1:基于唯一请求 ID(幂等 Token) 思路:前端生成 一个唯一的 requestId(…...
算法菜鸡备战3月2日传智杯省赛----0221
2209. 用地毯覆盖后的最少白色砖块 - 力扣(LeetCode) 力扣每日一题 class Solution { public:// 白色最少 黑色最多int minimumWhiteTiles(string floor, int numCarpets, int carpetLen) {int n floor.size();// 记忆化搜索vector memo(n 1, vector&…...
python pandas下载
pandas pandas:就是一个可以处理数据的 python 库 核心功能: 数据的清洗:处理丢失值,重复值数据分析:计算和统计信息,或分组汇总数据可视化:结合 图标库(Matplotlib)完成数据可视化…...
高斯牛顿法(GN)与列文伯格-马夸尔特方法在ORB-SLAM3中的应用
问题背景 高斯牛顿法(Gauss-Newton, GN)和列文伯格-马夸尔特方法(Levenburg-Marquadt, LM)是两种最常用的非线性优化方法,这两种方法在ORB-SLAM3系统中均有使用。 在ORB-SLAM3前端跟踪线程(Tracking)中,局…...
Python+Selenium+Pytest+POM自动化测试框架封装
🍅 点击文末小卡片 ,免费获取软件测试全套资料,资料在手,涨薪更快 1、测试框架简介 1)测试框架的优点 代码复用率高,如果不使用框架的话,代码会显得很冗余。可以组装日志、报告、邮件等一些高…...
猿大师中间件:网页直接内嵌本机EXE、OCX控件、ActiveX控件或桌面应用程序神器
猿大师中间件自从2019年发布以来,迄今为止不断迭代升级,给第三方提供了将自己的桌面程序和OCX控件支持直接内嵌到浏览器网页运行的赋能SDK开发包。 目前针对不同需求发布了三个成熟且商用的产品: 猿大师播放器:浏览器中直接原生…...
【Python】03-Python语法入门
文章目录 1、基本概念1.1、表达式1.2、语句1.3、程序(program)1.4、函数(function) 2、基本语法3、字面量与变量4、变量与标识符 1、基本概念 1.1、表达式 表达式就是一个类似于数学公式的东西,表达式一般仅用来计算一…...
C++,设计模式,【工厂方法模式】
文章目录 如何用汽车生产线理解工厂方法模式?一、传统生产方式的困境二、工厂方法模式解决方案三、模式应用场景四、模式优势分析五、现实应用启示✅C++,设计模式,【目录篇】 如何用汽车生产线理解工厂方法模式? 某个早晨,某车企CEO看着会议室里堆积如面的新车订单皱起眉…...
跟着 Lua 5.1 官方参考文档学习 Lua (5)
文章目录 2.10 – Garbage Collection2.10.1 – Garbage-Collection Metamethods2.10.2 – Weak Tables 2.10 – Garbage Collection Lua performs automatic memory management. This means that you have to worry neither about allocating memory for new objects nor abo…...
9.PG数据库层权限管理(pg系列课程)第2遍
一、PostgreSQL数据库属主 Postgres中的数据库属主属于创建者,只要有createdb的权限就可以创建数据库,数据库属主不一定拥有存放在该数据库中其它用户创建的对象的访问权限。数据库在创建后,允许public角色连接,即允许任何人连接…...
鸿蒙-canvas-画时钟
文章目录 前言准备分析组成部分数值计算过程 开始第一步 画圆环第二步 画格子第三步 画数字第四、五步 画指针&定时更新最后一步 前言 你在 Android 上能画出来的东西,在鸿蒙上画不出来? 画个时钟嘛,有啥难的? 你行你上&…...
【AI实践】阿里百炼文本对话Agent安卓版搭建
环境:安卓手机运行环境;WinsurfAI编程工具;阿里百炼提前创建Agent应用; 耗时:2小时; 1,新建安卓项目 完成文本输入,并将输入的文字显示出来。 2,安装SDK 参考文档 安…...
算法很美笔记(Java)——动态规划
解重叠子问题(当前解用到了以前求过的解) 形式:记忆型递归或递推(dp) 动态规划本质是递推,核心是找到状态转移的方式,也就是填excel表时的逻辑(填的方式),而…...
Jest单元测试
由于格式和图片解析问题,可前往 阅读原文 前端自动化测试在提高代码质量、减少错误、提高团队协作和加速交付流程方面发挥着重要作用。它是现代软件开发中不可或缺的一部分,可以帮助开发团队构建可靠、高质量的应用程序 单元测试(Unit Testi…...
《Stable Diffusion绘画完全指南:从入门到精通的Prompt设计艺术》-配套代码示例
第一章:模型加载与基础生成 1.1 基础模型加载 from diffusers import StableDiffusionPipeline import torch# 加载SD 1.5基础模型(FP32精度) pipe StableDiffusionPipeline.from_pretrained("runwayml/stable-diffusion-v1-5",…...
OnlyOffice:前端编辑器与后端API实现高效办公
OnlyOffice:前端编辑器与后端API实现高效办公 一、OnlyOffice概述二、前端编辑器:高效、灵活且易用1. 完善的编辑功能2. 实时协作支持3. 自动保存与版本管理4. 高度自定义的界面 三、后端API:管理文档、用户与权限1. 轻松集成与定制2. 实时协…...
springboot多实例部署时,@Scheduled注释的方法重复执行
问题:springboot多实例部署时,Scheduled注释的方法重复执行 在 Spring Boot 中要实现 Redis 的SET NX EX命令,可以借助 Spring Data Redis 来完成。SET NX EX命令用于在键不存在时设置键值对,并同时设置过期时间。 <dependen…...
coco格式
COCO(Common Objects in Context)格式是一种广泛用于图像识别和分割任务的数据格式,尤其是在目标检测、语义分割等任务中。COCO格式的核心包括以下几个部分: images: 包含图像的基本信息(如文件名、大小、ID等&#x…...
骶骨神经
骶骨肿瘤手术后遗症是什么_39健康网_癌症 [健康之路]匠心仁术(七) 勇闯禁区 骶骨肿瘤切除术...
Nacos学习(二)——继承Feign与Config中心
目录 一、集成Feign (一)基础用法 1.添加openfeign依赖 2. 开启openFeign注解扫描 3.创建ProviderService接口 4.修改ConsumerController (二)OpenFeign日志配置 (三)参数传递 1.参数传递的问题 2.参数传递的方式 2.1URL路径传参 2.2URL上拼接参数 2.3body传参 …...
计算机网络安全之一:网络安全概述
1.1 网络安全的内涵 随着计算机和网络技术的迅猛发展和广泛普及,越来越多的企业将经营的各种业务建立在Internet/Intranet环境中。于是,支持E-mail、文件共享、即时消息传送的消息和协作服务器成为当今商业社会中的极重要的IT基础设施。然而࿰…...
未来SLAM的研究方向和热点
SLAM(Simultaneous Localization and Mapping)是同时定位与地图构建的缩写,指的是机器人或设备在一个未知环境中一边进行自我定位,一边构建出环境的地图。SLAM广泛应用于机器人、自动驾驶、无人机等领域,涉及多个研究方…...
DuodooBMS源码解读之 purchase_change 模块
采购变更模块用户使用手册 一、模块概述 本扩展模块主要用于处理采购变更相关业务,包括采购变更单的创建、展示以及将采购变更信息导出为 Excel 文件等功能。以下将详细介绍该模块的具体使用方法。 二、模块功能及使用方法 (一)采购变更单…...
uniapp中引入Vant Weapp的保姆级教学(包含错误处理)
废话不多说,直接上方法,网上的教学好多都是错误的 1.安装vant weapp 在Hbuilder的终端,输入以下代码 npm install vant/weapp -S --production 2.新建wxcomponents文件夹 在项目的跟目录新建一个“wxcomponents’文件夹,与app.…...
Effective C++ 读书笔记(十二)
条款三十四:区分接口继承和实现继承 public继承由两部分组成:函数接口继承和函数实现继承。这两者的差异很像函数声明和函数定义之间的差异。 作为类的设计者,我们有时希望派生类只继承成员函数的接口(也就是函数声明࿰…...
【卡梅德生物】构建噬菌体文库与噬菌体展示文库构建服务新探索
在生命科学与生物技术快速发展的当下,抗体文库构建、构建噬菌体文库以及噬菌体展示文库构建服务在生物医药研发领域中占据着举足轻重的地位。它们不仅是基础研究的重要工具,更是推动抗体药物开发、疾病诊断技术进步的关键力量。 构建噬菌体文库是整个技…...
【JavaScript】《JavaScript高级程序设计 (第4版) 》笔记-Chapter19-表单脚本
十九、表单脚本 表单脚本 JavaScript 较早的一个用途是承担一部分服务器端表单处理的责任。虽然 Web 和 JavaScript 都已经发展了很多年,但 Web 表单的变化不是很大。由于不能直接使用表单解决问题,因此开发者不得不使用JavaScript 既做表单验证…...
C++STL容器之map
1.介绍 map是 C 标准模板库(STL)中的一个关联容器,用于存储键值对(key-value pairs)。map中的元素是按照键(key)进行排序的,并且每个键在容器中是唯一的。map通常基于红黑树…...
基于Nanopi duo2的WiFi智能摄像头
1.固件包烧录 https://wiki.friendlyelec.com/wiki/index.php/NanoPi_Duo2/zh#.E8.BF.9E.E6.8E.A5WiFi 固件包链接以及烧录工具都在上面链接中 烧录过程 使用读卡器将SD卡插入到电脑,然后打开烧录工具 2.通过串口工具连接板子使其连接WiFi 对应的串口工具,就是这个HyperT…...
Java 内存区域详解
1 常见面试题 1.1 基本问题 介绍下Java内存区域(运行时数据区)Java对象的创建过程(五步,建议能够默写出来并且要知道每一步虚拟机做了什么)对象的访问定位的两种方式(句柄和直接指针两种方式)…...
