You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

@RestController同步方法疑问:跨接口请求为何无法并行?

单例RestController中synchronized方法的线程锁定误区

我写了一个单例的@RestController,两个接口方法都加了synchronized修饰,但实际运行结果和预期不符,想搞清楚哪里理解错了。

Controller代码

@RestController
public class TestRestController {

    @GetMapping("/helloworld/{parm}")
    public synchronized void helloWorld(@PathVariable("parm") String data) throws Exception{

        System.out.println(Thread.currentThread().getName()+" helloworld ....Start"+new Date() +"  "+ data);
        Thread.sleep(5000);
        System.out.println(Thread.currentThread().getName()+" helloworld ....End"+ new Date());

    }

    @GetMapping("/hello/{parm}")
    public synchronized void hello(@PathVariable("parm") String data) throws Exception{

        System.out.println(Thread.currentThread().getName()+" hello ....Start"+new Date() +"  "+ data);
        Thread.sleep(10000);
        System.out.println(Thread.currentThread().getName()+" hello ....End"+ new Date());

    }
}

调用顺序

  1. 先请求 http://localhost:8080/hello/4
  2. 紧接着请求 http://localhost:8080/helloworld/3

实际输出

http-nio-8080-exec-1 hello ....StartWed Sep 13 21:23:37 IST 2023  4
http-nio-8080-exec-1 hello ....EndWed Sep 13 21:23:47 IST 2023
http-nio-8080-exec-2 helloworld ....StartWed Sep 13 21:23:47 IST 2023  3
http-nio-8080-exec-2 helloworld ....EndWed Sep 13 21:23:52 IST 2023

预期输出(忽略时间戳细节)

http-nio-8080-exec-1 hello ....StartWed Sep 13 21:23:37 IST 2023  4
http-nio-8080-exec-2 helloworld ....StartWed Sep 13 21:23:37 IST 2023  3
http-nio-8080-exec-2 helloworld ....EndWed Sep 13 21:23:42 IST 2023
http-nio-8080-exec-1 hello ....EndWed Sep 13 21:23:47 IST 2023

我的理解误区:我以为加了synchronized后,只有被请求的方法会被锁定,其他方法可以同时执行,但实际却是串行执行,这是为什么?


问题解析

核心错误:synchronized实例方法锁定的是对象实例,而非单个方法

当你把synchronized修饰在实例方法上时,本质是锁定了当前类的对象实例本身,而不是这个方法。因为Spring的@RestController默认是单例的,整个应用里只有一个TestRestController实例。

所以当第一个线程(exec-1)调用hello方法时,它会持有这个单例实例的锁,此时第二个线程(exec-2)调用helloworld方法时,因为这也是同一个实例的synchronized方法,必须等第一个线程释放锁(也就是hello方法执行完)之后,才能获取锁并执行,这就导致了两个接口请求串行执行,和你预期的并行不符。

实现预期并行效果的方案

如果想让两个方法各自锁定、互不影响,需要给每个方法分配独立的锁对象,比如在Controller里定义两个私有的锁对象,然后用synchronized代码块锁定对应的锁:

@RestController
public class TestRestController {
    // 为每个方法定义独立的锁对象
    private final Object helloLock = new Object();
    private final Object helloWorldLock = new Object();

    @GetMapping("/helloworld/{parm}")
    public void helloWorld(@PathVariable("parm") String data) throws Exception{
        synchronized (helloWorldLock) {
            System.out.println(Thread.currentThread().getName()+" helloworld ....Start"+new Date() +"  "+ data);
            Thread.sleep(5000);
            System.out.println(Thread.currentThread().getName()+" helloworld ....End"+ new Date());
        }
    }

    @GetMapping("/hello/{parm}")
    public void hello(@PathVariable("parm") String data) throws Exception{
        synchronized (helloLock) {
            System.out.println(Thread.currentThread().getName()+" hello ....Start"+new Date() +"  "+ data);
            Thread.sleep(10000);
            System.out.println(Thread.currentThread().getName()+" hello ....End"+ new Date());
        }
    }
}

这样修改后,两个方法的锁是独立的,一个线程调用hello时,另一个线程可以同时调用helloworld,就能达到你预期的并行执行效果。

内容的提问来源于stack exchange,提问作者Gourav

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.10 21:55:29