1. 并发控制的两种非Redis方案解析
在分布式系统和高并发场景下,数据一致性问题一直是开发者面临的重大挑战。虽然Redis凭借其高性能和丰富的原子操作常被用作分布式锁的首选方案,但在某些特定场景下(如基础设施限制、技术栈约束或简单业务场景),我们仍然需要掌握不依赖Redis的并发控制方法。以下是两种经过实战验证的非Redis解决方案:
1.1 数据库乐观锁实现并发控制
乐观锁的核心思想是"先操作,后冲突检测",适用于读多写少的场景。其实现主要依赖数据库表的版本号字段,以下是完整的实现步骤:
- 在需要并发控制的表中添加version字段,类型为整数或时间戳:
sql复制ALTER TABLE product ADD COLUMN version INT DEFAULT 0;
- 更新数据时采用版本号校验:
java复制// 伪代码示例
public boolean updateWithOptimisticLock(Product product) {
int oldVersion = product.getVersion();
product.setVersion(oldVersion + 1);
String sql = "UPDATE product SET stock = ?, version = ?
WHERE id = ? AND version = ?";
int affectedRows = jdbcTemplate.update(
sql,
product.getStock(),
product.getVersion(),
product.getId(),
oldVersion
);
return affectedRows > 0;
}
关键点:当多个线程同时更新同一条记录时,只有版本号匹配的更新会成功,其他线程需要捕获异常并重试或提示用户。
- 重试机制实现(采用Spring Retry示例):
java复制@Retryable(value = {OptimisticLockingFailureException.class},
maxAttempts = 3, backoff = @Backoff(delay = 100))
public void purchaseProduct(Long productId) {
// 业务逻辑
}
实战经验:
- 版本号字段建议使用BIGINT类型,避免整数溢出
- 在高冲突场景下,需要合理设置重试次数和退避策略
- 可结合@Transactional注解保证原子性,但要注意事务隔离级别(推荐READ_COMMITTED)
1.2 Java同步锁的精细化应用
对于单JVM内的并发控制,Java原生提供的同步机制仍然是轻量级且高效的选择。以下是几种典型实现方式的对比:
| 锁类型 | 适用场景 | 示例代码 | 注意事项 |
|---|---|---|---|
| synchronized | 方法级/块级同步 | public synchronized void update() {...} |
注意锁粒度,避免死锁 |
| ReentrantLock | 需要可中断、超时等高级特性 | lock.lock(); try {...} finally {lock.unlock();} |
必须手动释放锁 |
| StampedLock | 读多写少的高性能场景 | long stamp = lock.writeLock(); try {...} finally {lock.unlock(stamp);} |
不支持重入 |
线程池配置的黄金法则:
java复制ThreadPoolExecutor executor = new ThreadPoolExecutor(
Runtime.getRuntime().availableProcessors(), // 核心线程数
Runtime.getRuntime().availableProcessors() * 2, // 最大线程数
60L, TimeUnit.SECONDS, // 空闲线程存活时间
new LinkedBlockingQueue<>(1000), // 任务队列
new ThreadPoolExecutor.CallerRunsPolicy() // 拒绝策略
);
避坑指南:队列容量(queueCapacity)设置需要结合系统负载和硬件配置。经验公式:QPS × 平均处理时间(秒) × 2。例如系统QPS为100,平均处理时间50ms,则建议队列大小至少为100×0.05×2=10
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 定时任务的多实例运行方案
在Spring生态中,定时任务的并发执行常常会遇到各种意外情况。以下是实现多个定时任务稳定运行的完整解决方案:
2.1 Quartz调度器的正确配置
- 添加Maven依赖:
xml复制<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-quartz</artifactId>
</dependency>
- 配置任务持久化和集群模式(application.yml):
yaml复制spring:
quartz:
job-store-type: jdbc
properties:
org.quartz.scheduler.instanceId: AUTO
org.quartz.jobStore.isClustered: true
org.quartz.jobStore.clusterCheckinInterval: 20000
- 创建可并发的Job实现:
java复制@DisallowConcurrentExecution
public class InventoryCheckJob implements Job {
@Override
public void execute(JobExecutionContext context) {
// 实现业务逻辑
}
}
常见问题排查:
- 任务只执行最后一个:检查@Scheduled方法是否为private
- 任务不触发:确认@EnableScheduling注解已添加
- 任务重复执行:检查是否配置了集群模式
2.2 虚拟线程(Java 21+)的高并发任务处理
Java 21引入的虚拟线程为高并发任务处理提供了新思路:
java复制ExecutorService executor = Executors.newVirtualThreadPerTaskExecutor();
// 创建10000个并发任务
IntStream.range(0, 10000).forEach(i -> {
executor.submit(() -> {
// 任务逻辑
System.out.println("Task " + i + " running on: " + Thread.currentThread());
});
});
executor.shutdown();
性能对比测试数据(相同硬件环境):
| 任务类型 | 传统线程池(100线程) | 虚拟线程池 |
|---|---|---|
| 1000简单任务 | 1250ms | 320ms |
| 1000IO密集型 | 5600ms | 850ms |
| 内存占用 | 约120MB | 约45MB |
3. WebDriver并发测试的实战方案
在自动化测试场景中,WebDriver的并发执行需要特殊处理以避免资源冲突:
3.1 线程隔离的Driver管理
java复制public class WebDriverManager {
private static final ThreadLocal<WebDriver> driverHolder = new ThreadLocal<>();
public static WebDriver getDriver() {
if (driverHolder.get() == null) {
ChromeOptions options = new ChromeOptions();
options.addArguments("--headless");
driverHolder.set(new ChromeDriver(options));
}
return driverHolder.get();
}
public static void quitDriver() {
WebDriver driver = driverHolder.get();
if (driver != null) {
driver.quit();
driverHolder.remove();
}
}
}
3.2 使用Docker实现并行测试
- 创建Selenium Grid集群:
bash复制docker run -d -p 4444:4444 --name selenium-hub selenium/hub
docker run -d --shm-size="2g" --link selenium-hub:hub selenium/node-chrome
- 测试代码配置远程Driver:
java复制DesiredCapabilities capability = DesiredCapabilities.chrome();
WebDriver driver = new RemoteWebDriver(
new URL("http://localhost:4444/wd/hub"), capability);
性能优化技巧:
- 每个测试用例结束后清除cookies保持隔离
- 使用--shm-size参数避免Chrome崩溃
- 采用PageObject模式减少元素定位开销
4. 并发系统的监控与调优
没有监控的并发系统就像没有仪表的飞机。以下是关键监控指标的采集方案:
4.1 Prometheus + Grafana监控方案
- 应用端暴露指标(Spring Boot示例):
java复制@Bean
MeterRegistryCustomizer<MeterRegistry> metricsCommonTags() {
return registry -> registry.config().commonTags(
"application", "inventory-service");
}
@Timed(value = "order.process", description = "订单处理耗时")
public void processOrder(Order order) {
// 业务逻辑
}
- Grafana关键监控面板配置:
- 线程池活跃度:(active_threads / maximum_pool_size) * 100
- 任务队列饱和度:(queue_size / queue_capacity) * 100
- 任务拒绝率:rejected_tasks / total_tasks
4.2 并发瓶颈的定位方法
使用Arthas进行线上诊断:
bash复制# 查看线程阻塞情况
thread -b
# 监控方法调用耗时
monitor -c 5 com.example.Service *
# 追踪锁竞争
profiler start --event lock
调优经验法则:
- CPU密集型:线程数 = 核心数 + 1
- IO密集型:线程数 = 核心数 * (1 + 平均等待时间/平均计算时间)
- 混合型:通过压测找到最佳值
