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(…...
JS宏实例:数据透视工具的制作(四)
上一节中,我们完成了核心的计算代码部分,本节中将完善事件代码 一、创建所有需求的事件函数 1、窗体初始化 // 窗体初始化 function pivotForm_Initialize(){} function typeSet_Initialize(){} function valueSet_Initialize(){} function allCol…...
5. Go 方法(结构体的方法成员)
Go语言没有传统的 class ,为了让函数和结构体能够关联,Go引入了“方法”的概念。 当普通函数添加了接收者(receiver)后,就变成了方法。 一、函数和方法示例 // 普通函数 func Check(s string) string {return s }//…...
20250223学习记录
之前HDFview查看.hdf5文件的时候,看到土壤湿度数据是分为AM和PM,当时我有一个这样的疑问 但是后来用Python处理的时候,直接就是对整个的.hdf5文件处理,当时没有注意这一块,所以就没有这个疑问了。 今天突然看到一篇论…...
WPS携手DeepSeek:开启智能办公新时代
在数字化办公的浪潮中,效率与智能成为了人们追求的核心目标。近年来,人工智能技术的飞速发展为办公领域带来了前所未有的变革契机。DeepSeek作为一款备受瞩目的人工智能工具,以其强大的功能吸引了众多用户,然而在使用过程中&#…...
无需服务器,浏览器跑700+AI模型?!
Transformers.js 是一个创新的网络机器学习库,它将先进的 Transformer 模型直接带入浏览器,无需服务器端支持。这个库与 Hugging Face 的 Python transformers 库功能对等,提供相似的 API 接口来运行预训练模型,涵盖了自然语言处理…...
WSL2下ubuntu开启NFS服务
1. wsl2下ubuntu配置 安装 NFS 服务: sudo apt-get install nfs-kernel-server rpcbindnfs 配置文件/etc/exports: sudo vi /etc/exports打开/etc/exports 以后在后面添加如下所示内容: /home/mk/nfs *(rw,sync,no_subtree_check,no_root…...
深入了解 DevOps 基础架构:可追溯性的关键作用
在当今竞争激烈的软件环境中,快速交付强大的应用程序至关重要。尽管如此,在不影响质量的情况下保持速度可能是一项艰巨的任务,这就是 DevOps 中的可追溯性发挥作用的地方。通过提供软件开发生命周期 (SDLC) 的透明视图…...
k2路由器登录校园网
教程1刷入Breed,并手动刷入Padavan固件:斐讯K1、K2、K2P 刷机、刷入Breed 辅助工具 | tb (tbvv.net) Padavan下载网址: 我用的是: Padavan 登录的网址是 192.168.123.1 Padavan配置教程: 先用网线连上校园网&#…...
构建知识图谱的关键:高效三元组抽取技术在文本挖掘中的应用
在当今数字化时代,数据如潮水般涌来,文本数据更是海量且复杂。从科研论文到社交媒体动态,从新闻报道到电商商品描述,文本蕴含着丰富信息。而要让机器理解这些文本、挖掘有价值知识, “三元组抽取” 成为自然语言处理&a…...
超高清大图渲染性能优化实战:从页面卡死到流畅加载
目录 问题背景:1.为什么大图会导致页面卡死?一、DOM树构建(HTML Parsing)二、 资源加载:下载完整图片文件(可能高达30MB)三、解码处理(Decoding & Rasterization)、四…...
当使用vcpkg安装的qt5时,在VS调用出现libcrypto-*-x64.dll不是有效路径时
英文解决站点 applocal.ps1 fails in Visual Studio 2019 because of wildcard path in VcpkgAppLocalDLLs Issue #28614 microsoft/vcpkg 虽然这个bug不影响生成exe文件,第一次会弹出该错误,再次运行就正常,vcpkg会把对应的libcrypto-*-x64.dll版本复制到exe路径下..但是对…...
在 Vue 中处理跨域请求:全面解析与实践指南
在 Vue 中处理跨域请求:全面解析与实践指南 在现代 Web 开发的复杂生态中,跨域请求(CORS)如同一个无处不在的难题,时刻考验着开发者的技术能力。当我们构建基于 Vue.js 的前端应用时,这一问题尤为凸显。因为…...
标量化rknn的输入输出向量转换处理
这是一篇技术探索。yolo11模型生成后,我发现它无法在rknn环境正确识别出目标对象。而在宿主机上,或者直接调用.pt转换过的.onnx模型是可以得到正确结果的。这篇文章对应近乎一天的工作。最终的结论就是。这是一个模型量化的问题,与yolo的版本…...
认知重构 | 自我分化 | 苏格拉底式提问
注:本文为 “认知重构 | 自我分化” 相关文章合辑。 心理学上有一个词叫:认知重构(改变 “非黑即白,一分为二” 的思维方式) 原创 心理师威叔 心理自救 2024 年 10 月 26 日 19:08 广东 你有没有过这样的时候&#x…...
Java集合之ArrayList(含源码解析 超详细)
1.ArrayList简介 ArrayList的底层是数组队列,相当于动态数组。与Java中的数组相比,它的容量能动态增长。在添加大量元素前,应用程序可以使用ensureCapacity操作来增加ArrayList实例的容量。这可以减少递增式再分配的数量。 ArrayList继承于Ab…...
Java笔记18
2-10-3Cookie&Session 1.会话跟踪技术概述 会话:用户打开浏览器,访问web服务器的资源,会话建立,直到有一方断开连接,会话结束。在一次会话中可以包含多次请求和响应会话跟踪:一种维护浏览器状态的方法,服务器需要识别多次请求是否来自于同一浏览器,以便在同一次会话的多次…...
LangChain大模型应用开发:构建Agent智能体
介绍 大家好,博主又来给大家分享知识了。今天要给大家分享的内容是使用LangChain进行大模型应用开发中的构建Agent智能体。 在LangChain中,Agent智能体是一种能够根据输入的任务或问题,动态地决定使用哪些工具(如搜索引擎、数据库查询等)来…...
巧用GitHub的CICD功能免费打包部署前端项目
近年来,随着前端技术的发展,前端项目的构建和打包过程变得越来越复杂,占用的资源也越来越多。我有一台云服务器,原本打算使用Docker进行部署,以简化操作流程。然而,只要执行sudo docker-compose -f deploy/…...
【2】常用cmd命令大全、使用cmd运行和编译Java程序
文章目录 一、常用cmd命令大全文件和目录操作系统信息查看磁盘管理网络操作其他常用命令 二、使用cmd命令运行和编译Java程序 一、常用cmd命令大全 cmd的常用命令较多,java初学者只需了解这几个即可 dir:查看当前路径下的所有文件夹 cd:进入指…...
CMake基础:构建流程详解
目录 1.CMake构建过程的基本流程 2.CMake构建的具体步骤 2.1.创建构建目录 2.2.使用 CMake 生成构建文件 2.3.编译和构建 2.4.清理构建文件 2.5.重新配置和构建 3.跨平台构建示例 4.工具链与交叉编译 5.CMake构建后的项目结构解析 5.1.CMake构建后的目录结构 5.2.构…...
ardupilot 开发环境eclipse 中import 缺少C++
目录 文章目录 目录摘要1.修复过程摘要 本节主要解决ardupilot 开发环境eclipse 中import 缺少C++,无法导入ardupilot代码,会引起查看不方便的问题。如下图所示 1.修复过程 0.安装ubuntu 软件中自带的eclipse 1.打开eclipse—Help—install new software 2.在 Work with中…...
pikachu靶场通关笔记22-1 SQL注入05-1-insert注入(报错法)
目录 一、SQL注入 二、insert注入 三、报错型注入 四、updatexml函数 五、源码审计 六、insert渗透实战 1、渗透准备 2、获取数据库名database 3、获取表名table 4、获取列名column 5、获取字段 本系列为通过《pikachu靶场通关笔记》的SQL注入关卡(共10关࿰…...
人工智能--安全大模型训练计划:基于Fine-tuning + LLM Agent
安全大模型训练计划:基于Fine-tuning LLM Agent 1. 构建高质量安全数据集 目标:为安全大模型创建高质量、去偏、符合伦理的训练数据集,涵盖安全相关任务(如有害内容检测、隐私保护、道德推理等)。 1.1 数据收集 描…...
【堆垛策略】设计方法
堆垛策略的设计是积木堆叠系统的核心,直接影响堆叠的稳定性、效率和容错能力。以下是分层次的堆垛策略设计方法,涵盖基础规则、优化算法和容错机制: 1. 基础堆垛规则 (1) 物理稳定性优先 重心原则: 大尺寸/重量积木在下…...
云原生周刊:k0s 成为 CNCF 沙箱项目
开源项目推荐 HAMi HAMi(原名 k8s‑vGPU‑scheduler)是一款 CNCF Sandbox 级别的开源 K8s 中间件,通过虚拟化 GPU/NPU 等异构设备并支持内存、计算核心时间片隔离及共享调度,为容器提供统一接口,实现细粒度资源配额…...
Qt的学习(二)
1. 创建Hello Word 两种方式,实现helloworld: 1.通过图形化的方式,在界面上创建出一个控件,显示helloworld 2.通过纯代码的方式,通过编写代码,在界面上创建控件, 显示hello world; …...
算法刷题-回溯
今天给大家分享的还是一道关于dfs回溯的问题,对于这类问题大家还是要多刷和总结,总体难度还是偏大。 对于回溯问题有几个关键点: 1.首先对于这类回溯可以节点可以随机选择的问题,要做mian函数中循环调用dfs(i&#x…...
React 样式方案与状态方案初探
React 本身只提供了基础 UI 层开发范式,其他特性的支持需要借助相关社区方案实现。本文将介绍 React 应用体系中样式方案与状态方案的主流选择,帮助开发者根据项目需求做出合适的选择。 1. React 样式方案 1.1. 内联样式 (Inline Styles) 通过 style …...
Digital IC Design Flow
Flow介绍 1.设计规格 架构师根据市场需求制作算法模型(Algorithm emulation)及芯片架构(Chip architecture),确定芯片设计规格书(Chip design specification) 原型验证 原型验证(Prototype Validation)通常位于产品开发流程的前期阶段,主要是在设计和开发的初步阶…...
