前一篇文章我们讲了失败重试,本篇我们继续来看另一个新特性 - @ConcurrencyLimit并发限制,简单的说,它可以限制方法的并发访问数。
使用入门
@ConcurrencyLimit 跟@Retryable类似,可以加在单个方法上,设置方法级别的并发限制,也可以加在类上,给类中所有的通过代理调用的方法统一设置并发限制,使用起来非常简单,仅需2步:
- 在启动类添加
@EnableResilientMethods
java
@EnableResilientMethods
@SpringBootApplication
public class Springboot4Application {}
- 在业务类或者方法上添加
@ConcurrencyLimit
java
@ConcurrencyLimit(limit = 1)
@GetMapping(path = "/demo")
public String demo(){
log.info("ConcurrencyLimitController demo");
return "demo";
}
- limit = 1:说明本方法同时只允许1个并发访问,如果有多余的并发请求会一直阻塞等待。
- 加在controller的方法上等于实现了一个简易版的限流功能,可以对接口做一定程度的保护。
测试一下效果:
java
public static void main(String[] args) throws Exception{
System.out.println("start at " + LocalDateTime.now());
int threadCount = 5;
CountDownLatch start = new CountDownLatch(1);
CountDownLatch stop = new CountDownLatch(threadCount);
for(int i=0; i<threadCount; i++){
new Thread(()->{
try {
start.await();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
String res = new RestTemplate().getForObject("http://localhost:8080/api/concurrency-limit/demo", String.class);
System.out.println(res);
stop.countDown();
}).start();
}
start.countDown();
stop.await();
System.out.println("end at " + LocalDateTime.now());
}
我们使用5个线程并发访问前面的接口,因为接口只允许1个并发,因此总执行时长会超过5秒钟,日志如下:
plain
2026-01-23T12:52:43 INFO 11868 --- [nio-exec-4] c.g.x.s.c.ConcurrencyLimitController : ConcurrencyLimitController demo
2026-01-23T12:52:44 INFO 11868 --- [nio-exec-2] c.g.x.s.c.ConcurrencyLimitController : ConcurrencyLimitController demo
2026-01-23T12:52:45 INFO 11868 --- [nio-exec-5] c.g.x.s.c.ConcurrencyLimitController : ConcurrencyLimitController demo
2026-01-23T12:52:46 INFO 11868 --- [nio-exec-3] c.g.x.s.c.ConcurrencyLimitController : ConcurrencyLimitController demo
2026-01-23T12:52:47 INFO 11868 --- [nio-exec-1] c.g.x.s.c.ConcurrencyLimitController : ConcurrencyLimitController demo
实现原理
@ConcurrencyLimit背后是ConcurrencyThrottleSupport, 因此我们也可以用编程的方式手动的来做并发控制,比如我们可以利用现成的SyncTaskExecutor来实现与前面相同的并发限制功能:
java
SyncTaskExecutor taskExecutor = new SyncTaskExecutor(){
{
// 设置并发数
this.setConcurrencyLimit(1);
}
};
@GetMapping(path = "/demo2")
public String demo2()throws Exception{
return taskExecutor.execute(()->{
log.info("ConcurrencyLimitController demo2");
Thread.sleep(1000);
return "demo";
});
}
更多SpringBoot4.0的新特性请参考:SpringBoot4.0新特性